diff options
Diffstat (limited to 'source')
928 files changed, 25374 insertions, 14068 deletions
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ffc4d37f622..d0592e9a405 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -15,8 +15,9 @@ if(WITH_CLANG_TIDY AND NOT MSVC) endif() find_package(ClangTidy REQUIRED) - set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) - set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE}) + set(CMAKE_C_CLANG_TIDY + ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXECUTABLE};--extra-arg=-Wno-error=unknown-warning-option) endif() add_subdirectory(blender) diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index deff45d0350..8ba6e7318bb 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -89,6 +89,42 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_xr_types.h ) +set(SRC_DNA_DEFAULTS_INC + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_asset_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_collection_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curve_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_curves_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_fluid_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lattice_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_light_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_lightprobe_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_linestyle_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_material_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_mesh_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_meta_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_modifier_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_particle_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_pointcloud_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_scene_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_simulation_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_space_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_speaker_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vec_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_volume_defaults.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_defaults.h +) + add_subdirectory(datatoc) add_subdirectory(editors) add_subdirectory(windowmanager) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index f93cb8b2d64..a170f27d247 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1374,7 +1374,7 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) font->dpi = dpi; } else { - printf("The current font does not support the size, %f and dpi, %u\n", size, dpi); + printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); return false; } } diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 62bce36dda0..9dfcb1a4ad6 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -123,7 +123,7 @@ typedef struct GlyphCacheBLF { /* font size. */ float size; - /* and dpi. */ + /* and DPI. */ unsigned int dpi; bool bold; @@ -264,7 +264,7 @@ typedef struct FontBLF { /* the width to wrap the text, see BLF_WORD_WRAP */ int wrap_width; - /* font dpi (default 72). */ + /* Font DPI (default 72). */ unsigned int dpi; /* font size. */ @@ -276,7 +276,8 @@ typedef struct FontBLF { /* font options. */ int flags; - /* List of glyph caches (GlyphCacheBLF) for this font for size, dpi, bold, italic. + /** + * List of glyph caches (#GlyphCacheBLF) for this font for size, DPI, bold, italic. * Use blf_glyph_cache_acquire(font) and blf_glyph_cache_release(font) to access cache! */ ListBase cache; diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index fea5f515db8..59f0c86684d 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -46,14 +46,9 @@ * as it is and stick with using BMesh and CDDM. */ -#include "DNA_customdata_types.h" -#include "DNA_defs.h" -#include "DNA_meshdata_types.h" - #include "BLI_compiler_attrs.h" -#include "BKE_bvhutils.h" -#include "BKE_customdata.h" +#include "DNA_customdata_types.h" #ifdef __cplusplus extern "C" { @@ -125,7 +120,6 @@ struct DerivedMesh { /* Also called in Editmode */ int (*getNumVerts)(DerivedMesh *dm); int (*getNumEdges)(DerivedMesh *dm); - int (*getNumTessFaces)(DerivedMesh *dm); int (*getNumLoops)(DerivedMesh *dm); int (*getNumPolys)(DerivedMesh *dm); @@ -233,15 +227,6 @@ bool DM_release(DerivedMesh *dm); */ void DM_set_only_copy(DerivedMesh *dm, const struct CustomData_MeshMasks *mask); -/* Adds a vertex/edge/face custom data layer to a DerivedMesh, optionally - * backed by an external data array - * alloctype defines how the layer is allocated or copied, and how it is - * freed, see BKE_customdata.h for the different options. */ - -void DM_add_vert_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_edge_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); -void DM_add_poly_layer(struct DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer); - /* -------------------------------------------------------------------- */ /** \name Custom Data Layer Access Functions * diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index c7ea12a7065..050cfe1ed85 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -30,9 +30,8 @@ typedef enum AttributeDomain { ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */ ATTR_DOMAIN_CURVE = 4, /* A single curve in a larger curve data-block */ ATTR_DOMAIN_INSTANCE = 5, /* Instance */ - - ATTR_DOMAIN_NUM } AttributeDomain; +#define ATTR_DOMAIN_NUM 6 typedef enum AttributeDomainMask { ATTR_DOMAIN_MASK_POINT = (1 << 0), @@ -52,6 +51,7 @@ typedef enum AttributeDomainMask { /* Attributes. */ bool BKE_id_attributes_supported(struct ID *id); +bool BKE_attribute_allow_procedural_access(const char *attribute_name); /** * Create a new attribute layer. diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 8d449a124ec..c8c7c4c6808 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -77,6 +77,9 @@ class AttributeIDRef { friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); }; +bool allow_procedural_attribute_access(StringRef attribute_name); +extern const char *no_procedural_access_message; + } // namespace blender::bke /** diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 15fdc73adeb..ab13a2e85d0 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,15 +17,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 302 +#define BLENDER_VERSION 303 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 14 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 8f33003fa9d..d22abd235df 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -157,25 +157,6 @@ BVHTree *bvhtree_from_mesh_edges_ex(struct BVHTreeFromMesh *data, int tree_type, int axis); -/** - * Builds a BVH-tree where nodes are the given tessellated faces - * (NOTE: does not copy given mfaces!). - * \param vert_allocated: if true, vert freeing will be done when freeing data. - * \param face_allocated: if true, face freeing will be done when freeing data. - * \param faces_mask: if not null, true elements give which faces to add to BVH-tree. - * \param faces_num_active: if >= 0, number of active faces to add to BVH-tree - * (else will be computed from mask). - */ -BVHTree *bvhtree_from_mesh_faces_ex(struct BVHTreeFromMesh *data, - const struct MVert *vert, - const struct MFace *face, - int numFaces, - const BLI_bitmap *faces_mask, - int faces_num_active, - float epsilon, - int tree_type, - int axis); - BVHTree *bvhtree_from_editmesh_looptri( BVHTreeFromEditMesh *data, struct BMEditMesh *em, float epsilon, int tree_type, int axis); @@ -192,8 +173,6 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, /** * Builds a BVH-tree where nodes are the looptri faces of the given mesh. - * - * \note for edit-mesh this is currently a duplicate of #bvhtree_from_mesh_faces_ex */ BVHTree *bvhtree_from_mesh_looptri_ex(struct BVHTreeFromMesh *data, const struct MVert *vert, diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 57dc1e288dc..b7aa1c09e04 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -79,7 +79,7 @@ typedef struct CameraParams { void BKE_camera_params_init(CameraParams *params); void BKE_camera_params_from_object(CameraParams *params, const struct Object *cam_ob); void BKE_camera_params_from_view3d(CameraParams *params, - struct Depsgraph *depsgraph, + const struct Depsgraph *depsgraph, const struct View3D *v3d, const struct RegionView3D *rv3d); @@ -164,6 +164,14 @@ bool BKE_camera_multiview_spherical_stereo(const struct RenderData *rd, /* Camera background image API */ struct CameraBGImage *BKE_camera_background_image_new(struct Camera *cam); +/** + * Duplicate a background image, in a ID management compatible way. + * + * \param copy_flag The usual ID copying flags, see `LIB_ID_CREATE_`/`LIB_ID_COPY_` enums in + * `BKE_lib_id.h`. + */ +struct CameraBGImage *BKE_camera_background_image_copy(struct CameraBGImage *bgpic_src, + const int copy_flag); void BKE_camera_background_image_remove(struct Camera *cam, struct CameraBGImage *bgpic); void BKE_camera_background_image_clear(struct Camera *cam); diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index 0d4560207ea..d52fd91ccdd 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -129,6 +129,36 @@ bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap); void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size); void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size); +/** Get the minimum x value of each curve map table. */ +void BKE_curvemapping_get_range_minimums(const struct CurveMapping *curve_mapping, + float minimums[4]); + +/** Get the reciprocal of the difference between the maximum and the minimum x value of each curve + * map table. Evaluation parameters can be multiplied by this value to be normalized. If the + * difference is zero, 1^8 is returned. */ +void BKE_curvemapping_compute_range_dividers(const struct CurveMapping *curve_mapping, + float dividers[4]); + +/** Compute the slopes at the start and end points of each curve map. The slopes are multiplied by + * the range of the curve map to compensate for parameter normalization. If the slope is vertical, + * 1^8 is returned. */ +void BKE_curvemapping_compute_slopes(const struct CurveMapping *curve_mapping, + float start_slopes[4], + float end_slopes[4]); + +/** Check if the curve map at the index is identity, that is, does nothing. A curve map is said to + * be identity if: + * - The curve mapping uses extrapolation. + * - Its range is 1. + * - The slope at its start point is 1. + * - The slope at its end point is 1. + * - The number of points is 2. + * - The start point is at (0, 0). + * - The end point is at (1, 1). + * Note that this could return false even if the curve map is identity, this happens in the case + * when more than 2 points exist in the curve map but all points are collinear. */ +bool BKE_curvemapping_is_map_identity(const struct CurveMapping *curve_mapping, int index); + /** * Call when you do images etc, needs restore too. also verifies tables. * non-const (these modify the curve). diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index b3119666f0a..ce1f4c2b98c 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -67,7 +67,7 @@ typedef void (*ConstraintIDFunc)(struct bConstraint *con, * Callers of these functions must check that they actually point to something useful, * as some constraints don't define some of these. * - * Warning: + * WARNING: * it is not too advisable to reorder order of members of this struct, * as you'll have to edit quite a few #NUM_CONSTRAINT_TYPES of these * structs. diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 1e96c0e4c41..445f8d46f2d 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -48,6 +48,13 @@ struct BasisCache { * In other words, the index of the first control point that influences this evaluated point. */ Vector<int> start_indices; + + /** + * The result of #check_valid_num_and_order, to avoid retrieving its inputs later on. + * If this is true, the data above will be invalid, and original data should be copied + * to the evaluated result. + */ + bool invalid = false; }; } // namespace curves::nurbs @@ -120,7 +127,7 @@ class 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(int point_num, int curve_num); CurvesGeometry(const CurvesGeometry &other); CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); @@ -174,11 +181,18 @@ class CurvesGeometry : public ::CurvesGeometry { /** Update the cached count of curves of each type, necessary after #curve_types_for_write. */ void update_curve_types(); - bool has_curve_with_type(const CurveType type) const; + bool has_curve_with_type(CurveType type) const; /** Return true if all of the curves have the provided type. */ bool is_single_type(CurveType type) const; /** Return the number of curves with each type. */ const std::array<int, CURVE_TYPES_NUM> &curve_type_counts() const; + /** + * All of the curve indices for curves with a specific type. + */ + IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; + IndexMask indices_for_curve_type(CurveType type, + IndexMask selection, + Vector<int64_t> &r_indices) const; Span<float3> positions() const; MutableSpan<float3> positions_for_write(); @@ -267,6 +281,11 @@ class CurvesGeometry : public ::CurvesGeometry { Span<float2> surface_triangle_coords() const; MutableSpan<float2> surface_triangle_coords_for_write(); + VArray<float> selection_point_float() const; + MutableSpan<float> selection_point_float_for_write(); + VArray<float> selection_curve_float() const; + MutableSpan<float> selection_curve_float_for_write(); + /** * 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. @@ -276,11 +295,6 @@ class CurvesGeometry : public ::CurvesGeometry { bool bounds_min_max(float3 &min, float3 &max) const; private: - /** - * All of the curve indices for curves with a specific type. - */ - IndexMask indices_for_curve_type(CurveType type, Vector<int64_t> &r_indices) const; - /* -------------------------------------------------------------------- * Evaluation. */ @@ -397,6 +411,11 @@ class CurvesGeometry : public ::CurvesGeometry { */ GVArray adapt_domain(const GVArray &varray, AttributeDomain from, AttributeDomain to) const; + template<typename T> + VArray<T> adapt_domain(const VArray<T> &varray, AttributeDomain from, AttributeDomain to) const + { + return this->adapt_domain(GVArray(varray), from, to).typed<T>(); + } }; namespace curves { @@ -409,7 +428,7 @@ namespace curves { * The number of segments between control points, accounting for the last segment of cyclic * curves. The logic is simple, but this function should be used to make intentions clearer. */ -inline int curve_segment_size(const int points_num, const bool cyclic) +inline int curve_segment_num(const int points_num, const bool cyclic) { BLI_assert(points_num > 0); return (cyclic && points_num > 1) ? points_num : points_num - 1; @@ -444,7 +463,7 @@ void calculate_tangents(Span<float3> positions, bool is_cyclic, MutableSpan<floa /** * Calculate directions perpendicular to the tangent at every point by rotating an arbitrary - * starting vector by the same rotation of each tangent. If the curve is cylic, propagate a + * starting vector by the same rotation of each tangent. If the curve is cyclic, propagate a * correction through the entire to make sure the first and last normal align. */ void calculate_normals_minimum(Span<float3> tangents, bool cyclic, MutableSpan<float3> normals); @@ -474,10 +493,11 @@ bool segment_is_vector(Span<int8_t> handle_types_left, int segment_index); /** - * Return true if the curve's last cylic segment has a vector type. + * Return true if the curve's last cyclic segment has a vector type. * This only makes a difference in the shape of cyclic curves. */ -bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right); +bool last_cyclic_segment_is_vector(Span<int8_t> handle_types_left, + Span<int8_t> handle_types_right); /** * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector @@ -576,11 +596,11 @@ namespace catmull_rom { * \param points_num: The number of points in the curve. * \param resolution: The resolution for each segment. */ -int calculate_evaluated_size(int points_num, bool cyclic, int resolution); +int calculate_evaluated_num(int points_num, bool cyclic, int resolution); /** * Evaluate the Catmull Rom curve. The length of the #dst span should be calculated with - * #calculate_evaluated_size and is expected to divide evenly by the #src span's segment size. + * #calculate_evaluated_num and is expected to divide evenly by the #src span's segment size. */ void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst); @@ -597,7 +617,7 @@ namespace nurbs { /** * Checks the conditions that a NURBS curve needs to evaluate. */ -bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); +bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsMode knots_mode); /** * Calculate the standard evaluated size for a NURBS curve, using the standard that @@ -607,7 +627,7 @@ bool check_valid_size_and_order(int points_num, int8_t order, bool cyclic, Knots * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ -int calculate_evaluated_size( +int calculate_evaluated_num( int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); /** @@ -615,7 +635,7 @@ int calculate_evaluated_size( * The knots must be longer for a cyclic curve, for example, in order to provide weights for the * last evaluated points that are also influenced by the first control points. */ -int knots_size(int points_num, int8_t order, bool cyclic); +int knots_num(int points_num, int8_t order, bool cyclic); /** * Calculate the knots for a curve given its properties, based on built-in standards defined by @@ -635,7 +655,7 @@ void calculate_knots( * and a weight for each control point. */ void calculate_basis_cache(int points_num, - int evaluated_size, + int evaluated_num, int8_t order, bool cyclic, Span<float> knots, @@ -662,6 +682,7 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, } // namespace curves Curves *curves_new_nomain(int points_num, int curves_num); +Curves *curves_new_nomain(CurvesGeometry curves); /** * Create a new curves data-block containing a single curve with the given length and type. @@ -676,11 +697,11 @@ std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &typ inline int CurvesGeometry::points_num() const { - return this->point_size; + return this->point_num; } inline int CurvesGeometry::curves_num() const { - return this->curve_size; + return this->curve_num; } inline IndexRange CurvesGeometry::points_range() const { @@ -710,7 +731,7 @@ inline const std::array<int, CURVE_TYPES_NUM> &CurvesGeometry::curve_type_counts inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; @@ -720,7 +741,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const inline IndexRange CurvesGeometry::points_for_curves(const IndexRange curves) const { /* Offsets are not allocated when there are no curves. */ - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[curves.start()]; const int offset_next = this->curve_offsets[curves.one_after_last()]; @@ -742,7 +763,7 @@ inline IndexRange CurvesGeometry::evaluated_points_for_curve(int index) const inline IndexRange CurvesGeometry::evaluated_points_for_curves(const IndexRange curves) const { BLI_assert(!this->runtime->offsets_cache_dirty); - BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_num > 0); const int offset = this->runtime->evaluated_offsets_cache[curves.start()]; const int offset_next = this->runtime->evaluated_offsets_cache[curves.one_after_last()]; return {offset, offset_next - offset}; @@ -760,7 +781,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index, BLI_assert(cyclic == this->cyclic()[curve_index]); const IndexRange points = this->evaluated_points_for_curve(curve_index); const int start = points.start() + curve_index; - return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)}; + return {start, curves::curve_segment_num(points.size(), cyclic)}; } inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index, @@ -775,8 +796,10 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in const bool cyclic) const { const Span<float> lengths = this->evaluated_lengths_for_curve(curve_index, cyclic); - /* Check for curves that have no evaluated segments. */ - return lengths.is_empty() ? 0.0f : lengths.last(); + if (lengths.is_empty()) { + return 0.0f; + } + return lengths.last(); } /** \} */ diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh new file mode 100644 index 00000000000..62b060093e9 --- /dev/null +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.hh" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +namespace blender::bke::curves { + +/** + * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. + */ +void fill_curve_counts(const bke::CurvesGeometry &curves, + Span<IndexRange> curve_ranges, + MutableSpan<int> counts); + +/** + * Turn an array of sizes into the offset at each index including all previous sizes. + */ +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, int start_offset = 0); + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index f05dfb164cf..64c49830dc5 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -10,6 +10,10 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" +#ifdef __cplusplus +# include "BLI_span.hh" +# include "BLI_vector.hh" +#endif #include "DNA_customdata_types.h" @@ -700,39 +704,33 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap, /* .blend file I/O */ +#ifdef __cplusplus + /** * Prepare given custom data for file writing. * - * \param data: the custom-data to tweak for .blend file writing (modified in place). - * \param r_write_layers: contains a reduced set of layers to be written to file, - * use it with #writestruct_at_address() - * (caller must free it if != \a write_layers_buff). - * - * \param write_layers_buff: An optional buffer for r_write_layers (to avoid allocating it). - * \param write_layers_size: The size of pre-allocated \a write_layer_buff. + * \param data: The custom-data to tweak for .blend file writing (modified in place). + * \param layers_to_write: A reduced set of layers to be written to file. * - * \warning After this function has ran, given custom data is no more valid from Blender POV - * (its `totlayer` is invalid). This function shall always be called with localized data - * (as it is in write_meshes()). - * - * \note `data->typemap` is not updated here, since it is always rebuilt on file read anyway. - * This means written `typemap` does not match written layers (as returned by \a r_write_layers). - * Trivial to fix is ever needed. + * \warning This function invalidates the custom data struct by changing the layer counts and the + * #layers pointer, and by invalidating the type map. It expects to work on a shallow copy of + * the struct. */ -void CustomData_blend_write_prepare(struct CustomData *data, - struct CustomDataLayer **r_write_layers, - struct CustomDataLayer *write_layers_buff, - size_t write_layers_size); +void CustomData_blend_write_prepare(CustomData &data, + blender::Vector<CustomDataLayer, 16> &layers_to_write); /** - * \param layers: The layers argument assigned by #CustomData_blend_write_prepare. + * \param layers_to_write: Layers created by #CustomData_blend_write_prepare. */ -void CustomData_blend_write(struct BlendWriter *writer, - struct CustomData *data, - CustomDataLayer *layers, +void CustomData_blend_write(BlendWriter *writer, + CustomData *data, + blender::Span<CustomDataLayer> layers_to_write, int count, CustomDataMask cddata_mask, - struct ID *id); + ID *id); + +#endif + void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); #ifndef NDEBUG diff --git a/source/blender/blenkernel/BKE_customdata_file.h b/source/blender/blenkernel/BKE_customdata_file.h index 9d45d28bd18..6972dcb8681 100644 --- a/source/blender/blenkernel/BKE_customdata_file.h +++ b/source/blender/blenkernel/BKE_customdata_file.h @@ -25,17 +25,17 @@ void cdf_free(CDataFile *cdf); /* File read/write/remove */ -bool cdf_read_open(CDataFile *cdf, const char *filename); +bool cdf_read_open(CDataFile *cdf, const char *filepath); bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay); bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data); void cdf_read_close(CDataFile *cdf); -bool cdf_write_open(CDataFile *cdf, const char *filename); +bool cdf_write_open(CDataFile *cdf, const char *filepath); bool cdf_write_layer(CDataFile *cdf, CDataFileLayer *blay); bool cdf_write_data(CDataFile *cdf, unsigned int size, void *data); void cdf_write_close(CDataFile *cdf); -void cdf_remove(const char *filename); +void cdf_remove(const char *filepath); /* Layers */ diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 1b225884b14..a21d9141ac0 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -228,39 +228,44 @@ void BKE_defvert_normalize_lock_map(struct MDeformVert *dvert, /* Utilities to 'extract' a given vgroup into a simple float array, * for verts, but also edges/polys/loops. */ -void BKE_defvert_extract_vgroup_to_vertweights( - struct MDeformVert *dvert, int defgroup, int num_verts, float *r_weights, bool invert_vgroup); +void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert, + int defgroup, + int num_verts, + bool invert_vgroup, + float *r_weights); /** * The following three make basic interpolation, * using temp vert_weights array to avoid looking up same weight several times. */ -void BKE_defvert_extract_vgroup_to_edgeweights(struct MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_edgeweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MEdge *edges, int num_edges, - float *r_weights, - bool invert_vgroup); -void BKE_defvert_extract_vgroup_to_loopweights(struct MDeformVert *dvert, + bool invert_vgroup, + float *r_weights); +void BKE_defvert_extract_vgroup_to_loopweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MLoop *loops, int num_loops, - float *r_weights, - bool invert_vgroup); -void BKE_defvert_extract_vgroup_to_polyweights(struct MDeformVert *dvert, + bool invert_vgroup, + float *r_weights); +void BKE_defvert_extract_vgroup_to_polyweights(const struct MDeformVert *dvert, int defgroup, int num_verts, struct MLoop *loops, int num_loops, struct MPoly *polys, int num_polys, - float *r_weights, - bool invert_vgroup); + bool invert_vgroup, + float *r_weights); void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight); -void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDeformVert *dvlist); +void BKE_defvert_blend_write(struct BlendWriter *writer, + int count, + const struct MDeformVert *dvlist); void BKE_defvert_blend_read(struct BlendDataReader *reader, int count, struct MDeformVert *mdverts); diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 6784a1296e9..78d80ce200e 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -293,7 +293,7 @@ struct FCurve *BKE_fcurve_find_by_rna(struct PointerRNA *ptr, * temp hack needed for complex paths like texture ones. */ struct FCurve *BKE_fcurve_find_by_rna_context_ui(struct bContext *C, - struct PointerRNA *ptr, + const struct PointerRNA *ptr, struct PropertyRNA *prop, int rnaindex, struct AnimData **r_animdata, diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 36b382feb5f..9c86ab262ef 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -162,4 +162,14 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +class CurveLengthFieldInput final : public GeometryFieldInput { + public: + CurveLengthFieldInput(); + GVArray get_varray_for_context(const GeometryComponent &component, + AttributeDomain domain, + IndexMask mask) const final; + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index dfd9fccebbd..c58420efceb 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -98,7 +98,7 @@ class GeometryComponent { /** * Return the length of a specific domain, or 0 if the domain is not supported. */ - virtual int attribute_domain_size(AttributeDomain domain) const; + virtual int attribute_domain_num(AttributeDomain domain) const; /** * Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain @@ -521,11 +521,11 @@ struct GeometrySet { }; /** - * A geometry component that can store a mesh, storing the #Mesh data structure. + * A geometry component that can store a mesh, using the #Mesh data-block. * - * Attributes are stored in the mesh itself, on any of the four attribute domains. Generic - * attributes are stored in contiguous arrays, but often built-in attributes are stored in an - * array of structs fashion for historical reasons, requiring more complex attribute access. + * Attributes are stored, on any of the four attribute domains. Generic attributes are stored in + * contiguous arrays, but often built-in attributes are stored in an array of structs fashion for + * historical reasons, requiring more complex attribute access. */ class MeshComponent : public GeometryComponent { private: @@ -560,7 +560,7 @@ class MeshComponent : public GeometryComponent { */ Mesh *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -623,7 +623,7 @@ class PointCloudComponent : public GeometryComponent { */ PointCloud *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -664,7 +664,7 @@ class CurveComponentLegacy : public GeometryComponent { const CurveEval *get_for_read() const; CurveEval *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -682,8 +682,9 @@ class CurveComponentLegacy : public GeometryComponent { }; /** - * A geometry component that stores a group of curves, corresponding the #Curves and - * #CurvesGeometry types. + * A geometry component that stores a group of curves, corresponding the #Curves data-block type + * and the #CurvesGeometry type. Attributes are are stored on the control point domain and the + * curve domain. */ class CurveComponent : public GeometryComponent { private: @@ -715,7 +716,7 @@ class CurveComponent : public GeometryComponent { const Curves *get_for_read() const; Curves *get_for_write(); - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; bool is_empty() const final; @@ -949,8 +950,8 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan<blender::float4x4> instance_transforms(); blender::Span<blender::float4x4> instance_transforms() const; - int instances_amount() const; - int references_amount() const; + int instances_num() const; + int references_num() const; /** * Remove the indices that are not contained in the mask input, and remove unused instance @@ -963,7 +964,7 @@ class InstancesComponent : public GeometryComponent { blender::bke::CustomDataAttributes &attributes(); const blender::bke::CustomDataAttributes &attributes() const; - int attribute_domain_size(AttributeDomain domain) const final; + int attribute_domain_num(AttributeDomain domain) const final; void foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 06feb07aef2..96b6f7a53b0 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -81,6 +81,7 @@ typedef struct Global { * * 1 - 30: EEVEE debug/stats values (01/2018). * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). + * * 102: Enable extra items in string search UI (05/2022). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). * * 799: Enable some mysterious new depsgraph behavior (05/2015). diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index db45e405e79..8d9351806c4 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -172,7 +172,7 @@ bool BKE_previewimg_id_supports_jobs(const struct ID *id); /** * Trigger deferred loading of a custom image file into the preview buffer. */ -void BKE_previewimg_id_custom_set(struct ID *id, const char *path); +void BKE_previewimg_id_custom_set(struct ID *id, const char *filepath); /** * Free the preview image belonging to the id. @@ -223,11 +223,12 @@ struct PreviewImage *BKE_previewimg_cached_get(const char *name); struct PreviewImage *BKE_previewimg_cached_ensure(const char *name); /** - * Generate a #PreviewImage from given file path, using thumbnails management, if not yet existing. - * Does not actually generate the preview, #BKE_previewimg_ensure() must be called for that. + * Generate a #PreviewImage from given `filepath`, using thumbnails management, if not yet + * existing. Does not actually generate the preview, #BKE_previewimg_ensure() must be called for + * that. */ struct PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, - const char *path, + const char *filepath, int source, bool force_update); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 0417f335d11..1f131568900 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -273,14 +273,6 @@ bool BKE_image_is_openexr(struct Image *ima); void BKE_image_backup_render(struct Scene *scene, struct Image *ima, bool free_current_slot); /** - * For single-layer OpenEXR saving. - */ -bool BKE_image_save_openexr_multiview(struct Image *ima, - struct ImBuf *ibuf, - const char *filepath, - int flags); - -/** * Goes over all textures that use images. */ void BKE_image_free_all_textures(struct Main *bmain); @@ -392,7 +384,7 @@ void BKE_image_ensure_tile_token(char *filename); /** * When provided with an absolute virtual `filepath`, check to see if at least * one concrete file exists. - * Note: This function requires directory traversal and may be inefficient in time-critical, + * NOTE: This function requires directory traversal and may be inefficient in time-critical, * or iterative, code paths. */ bool BKE_image_tile_filepath_exists(const char *filepath); @@ -459,7 +451,7 @@ bool BKE_image_is_dirty_writable(struct Image *image, bool *r_is_writable); int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); bool BKE_image_has_packedfile(const struct Image *image); -bool BKE_image_has_filepath(struct Image *ima); +bool BKE_image_has_filepath(const struct Image *ima); /** * Checks the image buffer changes with time (not keyframed values). */ diff --git a/source/blender/blenkernel/BKE_image_format.h b/source/blender/blenkernel/BKE_image_format.h index 633af54ea4f..6a03d1d8df5 100644 --- a/source/blender/blenkernel/BKE_image_format.h +++ b/source/blender/blenkernel/BKE_image_format.h @@ -76,6 +76,8 @@ char BKE_imtype_from_arg(const char *arg); void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf); void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf); +bool BKE_image_format_is_byte(const struct ImageFormatData *imf); + /* Color Management */ void BKE_image_format_color_management_copy(struct ImageFormatData *imf, diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h index 052fc937af9..673a7dffb82 100644 --- a/source/blender/blenkernel/BKE_image_save.h +++ b/source/blender/blenkernel/BKE_image_save.h @@ -13,10 +13,11 @@ extern "C" { #endif struct Image; +struct ImageUser; struct Main; +struct RenderResult; struct ReportList; struct Scene; -struct RenderResult; /* Image datablock saving. */ @@ -34,11 +35,20 @@ typedef struct ImageSaveOptions { bool save_copy; bool save_as_render; bool do_newpath; + + /* Keep track of previous values for auto updates in UI. */ + bool prev_save_as_render; + int prev_imtype; } ImageSaveOptions; -void BKE_image_save_options_init(struct ImageSaveOptions *opts, +bool BKE_image_save_options_init(ImageSaveOptions *opts, struct Main *bmain, - struct Scene *scene); + struct Scene *scene, + struct Image *ima, + struct ImageUser *iuser, + const bool guess_path, + const bool save_as_render); +void BKE_image_save_options_update(struct ImageSaveOptions *opts, struct Image *ima); void BKE_image_save_options_free(struct ImageSaveOptions *opts); bool BKE_image_save(struct ReportList *reports, diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 504ab47c1c5..beac608a138 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -603,7 +603,7 @@ bool BKE_id_can_be_asset(const struct ID *id); * we should either cache that status info also in virtual override IDs, or address the * long-standing TODO of getting an efficient 'owner_id' access for all embedded ID types. */ -bool BKE_id_is_editable(struct Main *bmain, struct ID *id); +bool BKE_id_is_editable(const struct Main *bmain, const struct ID *id); /** * Returns ordered list of data-blocks for display in the UI. diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index dfb2b900b10..86630a6ab44 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -62,12 +62,12 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_ /** * Check if given ID has some override rules that actually indicate the user edited it. */ -bool BKE_lib_override_library_is_user_edited(struct ID *id); +bool BKE_lib_override_library_is_user_edited(const struct ID *id); /** * Check if given ID is a system override. */ -bool BKE_lib_override_library_is_system_defined(struct Main *bmain, struct ID *id); +bool BKE_lib_override_library_is_system_defined(const struct Main *bmain, const struct ID *id); /** * Check if given ID is a leaf in its liboverride hierarchy (i.e. if it does not use any other @@ -116,6 +116,8 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * \param do_no_main: Create the new override data outside of Main database. * Used for resyncing of linked overrides. * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * * \return \a true on success, \a false otherwise. */ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, @@ -123,7 +125,8 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, const struct ID *id_root_reference, struct ID *id_hierarchy_root, const struct ID *id_hierarchy_root_reference, - bool do_no_main); + bool do_no_main, + const bool do_fully_editable); /** * Advanced 'smart' function to create fully functional overrides. * @@ -154,6 +157,8 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * + * \param do_fully_editable: if true, tag all created overrides as user-editable by default. + * * \return true if override was successfully created. */ bool BKE_lib_override_library_create(struct Main *bmain, @@ -163,7 +168,8 @@ bool BKE_lib_override_library_create(struct Main *bmain, struct ID *id_root_reference, struct ID *id_hierarchy_root_reference, struct ID *id_instance_hint, - struct ID **r_id_root_override); + struct ID **r_id_root_override, + const bool do_fully_editable); /** * Create a library override template. */ diff --git a/source/blender/blenkernel/BKE_lib_principle_properties.h b/source/blender/blenkernel/BKE_lib_principle_properties.h index 10d0d33a0c4..42177204efb 100644 --- a/source/blender/blenkernel/BKE_lib_principle_properties.h +++ b/source/blender/blenkernel/BKE_lib_principle_properties.h @@ -40,7 +40,7 @@ struct ReportList; struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id); #if 0 /** - * Shallow or deep copy of a whole princple properties from \a src_id to \a dst_id. + * Shallow or deep copy of a whole principle properties from \a src_id to \a dst_id. */ void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy); #endif diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index f38f6afff7e..05e502f06c2 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -52,7 +52,7 @@ void BKE_object_material_remap_calc(struct Object *ob_dst, */ void BKE_object_material_from_eval_data(struct Main *bmain, struct Object *ob_orig, - struct ID *data_eval); + const struct ID *data_eval); struct Material *BKE_material_add(struct Main *bmain, const char *name); struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name); void BKE_gpencil_material_attr_init(struct Material *ma); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 091f30825ae..23ec69babc8 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -204,6 +204,7 @@ bool BKE_mesh_material_index_used(struct Mesh *me, short index); void BKE_mesh_material_index_clear(struct Mesh *me); void BKE_mesh_material_remap(struct Mesh *me, const unsigned int *remap, unsigned int remap_len); void BKE_mesh_smooth_flag_set(struct Mesh *me, bool use_smooth); +void BKE_mesh_auto_smooth_flag_set(struct Mesh *me, bool use_auto_smooth, float auto_smooth_angle); /** * Needed after converting a mesh with subsurf optimal display to mesh. @@ -931,13 +932,6 @@ void BKE_mesh_flush_select_from_polys_ex(struct MVert *mvert, const struct MPoly *mpoly, int totpoly); void BKE_mesh_flush_select_from_polys(struct Mesh *me); -void BKE_mesh_flush_select_from_verts_ex(const struct MVert *mvert, - int totvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_select_from_verts(struct Mesh *me); /* spatial evaluation */ diff --git a/source/blender/blenkernel/BKE_mesh_tangent.h b/source/blender/blenkernel/BKE_mesh_tangent.h index 27061a5766e..58142653a90 100644 --- a/source/blender/blenkernel/BKE_mesh_tangent.h +++ b/source/blender/blenkernel/BKE_mesh_tangent.h @@ -21,7 +21,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const struct MVert *mverts, int numVerts, const struct MLoop *mloops, float (*r_looptangent)[4], - float (*loopnors)[3], + const float (*loopnors)[3], const struct MLoopUV *loopuv, int numLoops, const struct MPoly *mpolys, diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 881e86ccc54..866b0353d07 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -102,6 +102,7 @@ typedef enum { /** Accepts #BMesh input (without conversion). */ eModifierTypeFlag_AcceptsBMesh = (1 << 11), } ModifierTypeFlag; +ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh) typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); typedef void (*TexWalkFunc)(void *userData, @@ -113,7 +114,7 @@ typedef enum ModifierApplyFlag { /** Render time. */ MOD_APPLY_RENDER = 1 << 0, /** Result of evaluation will be cached, so modifier might - * want to cache data for quick updates (used by subsurf) */ + * want to cache data for quick updates (used by subdivision-surface) */ MOD_APPLY_USECACHE = 1 << 1, /** Modifier evaluated for undeformed texture coordinates */ MOD_APPLY_ORCO = 1 << 2, @@ -363,7 +364,9 @@ typedef struct ModifierTypeInfo { * This method should write any additional arrays and referenced structs that should be * stored in the file. */ - void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md); + void (*blendWrite)(struct BlendWriter *writer, + const struct ID *id_owner, + const struct ModifierData *md); /** * Is called when the modifier is read from a file. @@ -592,7 +595,9 @@ struct Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(struct Object void BKE_modifier_check_uuids_unique_and_report(const struct Object *object); -void BKE_modifier_blend_write(struct BlendWriter *writer, struct ListBase *modbase); +void BKE_modifier_blend_write(struct BlendWriter *writer, + const struct ID *id_owner, + struct ListBase *modbase); void BKE_modifier_blend_read_data(struct BlendDataReader *reader, struct ListBase *lb, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 0efe38c1e8f..dfa330ff508 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -130,7 +130,7 @@ void multiresModifier_prepare_join(struct Depsgraph *depsgraph, struct Object *ob, struct Object *to_ob); -int multires_mdisp_corners(struct MDisps *s); +int multires_mdisp_corners(const struct MDisps *s); /** * Update multi-res data after topology changing. diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c9228db9ecc..1ff10d06b00 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1090,8 +1090,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_SQUEEZE 117 //#define SH_NODE_MATERIAL_EXT 118 #define SH_NODE_INVERT 119 -#define SH_NODE_SEPRGB 120 -#define SH_NODE_COMBRGB 121 +#define SH_NODE_SEPRGB_LEGACY 120 +#define SH_NODE_COMBRGB_LEGACY 121 #define SH_NODE_HUE_SAT 122 #define SH_NODE_OUTPUT_MATERIAL 124 @@ -1147,8 +1147,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_WAVELENGTH 180 #define SH_NODE_BLACKBODY 181 #define SH_NODE_VECT_TRANSFORM 182 -#define SH_NODE_SEPHSV 183 -#define SH_NODE_COMBHSV 184 +#define SH_NODE_SEPHSV_LEGACY 183 +#define SH_NODE_COMBHSV_LEGACY 184 #define SH_NODE_BSDF_HAIR 185 // #define SH_NODE_LAMP 186 #define SH_NODE_UVMAP 187 @@ -1175,6 +1175,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_VECTOR_ROTATE 708 #define SH_NODE_CURVE_FLOAT 709 #define SH_NODE_POINT_INFO 710 +#define SH_NODE_COMBINE_COLOR 711 +#define SH_NODE_SEPARATE_COLOR 712 /** \} */ @@ -1202,8 +1204,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_MAP_VALUE 213 #define CMP_NODE_TIME 214 #define CMP_NODE_VECBLUR 215 -#define CMP_NODE_SEPRGBA 216 -#define CMP_NODE_SEPHSVA 217 +#define CMP_NODE_SEPRGBA_LEGACY 216 +#define CMP_NODE_SEPHSVA_LEGACY 217 #define CMP_NODE_SETALPHA 218 #define CMP_NODE_HUE_SAT 219 #define CMP_NODE_IMAGE 220 @@ -1213,14 +1215,14 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_TEXTURE 224 #define CMP_NODE_TRANSLATE 225 #define CMP_NODE_ZCOMBINE 226 -#define CMP_NODE_COMBRGBA 227 +#define CMP_NODE_COMBRGBA_LEGACY 227 #define CMP_NODE_DILATEERODE 228 #define CMP_NODE_ROTATE 229 #define CMP_NODE_SCALE 230 -#define CMP_NODE_SEPYCCA 231 -#define CMP_NODE_COMBYCCA 232 -#define CMP_NODE_SEPYUVA 233 -#define CMP_NODE_COMBYUVA 234 +#define CMP_NODE_SEPYCCA_LEGACY 231 +#define CMP_NODE_COMBYCCA_LEGACY 232 +#define CMP_NODE_SEPYUVA_LEGACY 233 +#define CMP_NODE_COMBYUVA_LEGACY 234 #define CMP_NODE_DIFF_MATTE 235 #define CMP_NODE_COLOR_SPILL 236 #define CMP_NODE_CHROMA_MATTE 237 @@ -1232,7 +1234,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_ID_MASK 243 #define CMP_NODE_DEFOCUS 244 #define CMP_NODE_DISPLACE 245 -#define CMP_NODE_COMBHSVA 246 +#define CMP_NODE_COMBHSVA_LEGACY 246 #define CMP_NODE_MATH 247 #define CMP_NODE_LUMA_MATTE 248 #define CMP_NODE_BRIGHTCONTRAST 249 @@ -1289,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define CMP_NODE_SCENE_TIME 329 #define CMP_NODE_SEPARATE_XYZ 330 #define CMP_NODE_COMBINE_XYZ 331 +#define CMP_NODE_COMBINE_COLOR 332 +#define CMP_NODE_SEPARATE_COLOR 333 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -1354,11 +1358,13 @@ struct TexResult; #define TEX_NODE_TRANSLATE 416 #define TEX_NODE_COORD 417 #define TEX_NODE_DISTANCE 418 -#define TEX_NODE_COMPOSE 419 -#define TEX_NODE_DECOMPOSE 420 +#define TEX_NODE_COMPOSE_LEGACY 419 +#define TEX_NODE_DECOMPOSE_LEGACY 420 #define TEX_NODE_VALTONOR 421 #define TEX_NODE_SCALE 422 #define TEX_NODE_AT 423 +#define TEX_NODE_COMBINE_COLOR 424 +#define TEX_NODE_SEPARATE_COLOR 425 /* 501-599 reserved. Use like this: TEX_NODE_PROC + TEX_CLOUDS, etc */ #define TEX_NODE_PROC 500 @@ -1511,6 +1517,8 @@ struct TexResult; #define FN_NODE_REPLACE_STRING 1218 #define FN_NODE_INPUT_BOOL 1219 #define FN_NODE_INPUT_INT 1220 +#define FN_NODE_SEPARATE_COLOR 1221 +#define FN_NODE_COMBINE_COLOR 1222 /** \} */ diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh new file mode 100644 index 00000000000..f5fb53f962b --- /dev/null +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_sys_types.h" +#include "BLI_utility_mixins.hh" + +namespace blender::nodes { +struct FieldInferencingInterface; +class NodeDeclaration; +} // namespace blender::nodes + +namespace blender::bke { + +class bNodeTreeRuntime : NonCopyable, NonMovable { + public: + /** + * Keeps track of what changed in the node tree until the next update. + * Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`. + * #eNodeTreeChangedFlag. + */ + uint32_t changed_flag = 0; + /** + * A hash of the topology of the node tree leading up to the outputs. This is used to determine + * of the node tree changed in a way that requires updating geometry nodes or shaders. + */ + uint32_t output_topology_hash = 0; + + /** + * Used to cache run-time information of the node tree. + * #eNodeTreeRuntimeFlag. + */ + uint8_t runtime_flag = 0; + + /** Information about how inputs and outputs of the node group interact with fields. */ + std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface; +}; + +/** + * Run-time data for every socket. This should only contain data that is somewhat persistent (i.e. + * data that lives longer than a single depsgraph evaluation + redraw). Data that's only used in + * smaller scopes should generally be stored in separate arrays and/or maps. + */ +class bNodeSocketRuntime : NonCopyable, NonMovable { + public: + /** + * References a socket declaration that is owned by `node->declaration`. This is only runtime + * data. It has to be updated when the node declaration changes. + */ + const SocketDeclarationHandle *declaration = nullptr; + + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag = 0; +}; + +/** + * Run-time data for every node. This should only contain data that is somewhat persistent (i.e. + * data that lives longer than a single depsgraph evaluation + redraw). Data that's only used in + * smaller scopes should generally be stored in separate arrays and/or maps. + */ +class bNodeRuntime : NonCopyable, NonMovable { + public: + /** + * Describes the desired interface of the node. This is run-time data only. + * The actual interface of the node may deviate from the declaration temporarily. + * It's possible to sync the actual state of the node to the desired state. Currently, this is + * only done when a node is created or loaded. + * + * In the future, we may want to keep more data only in the declaration, so that it does not have + * to be synced to other places that are stored in files. That especially applies to data that + * can't be edited by users directly (e.g. min/max values of sockets, tooltips, ...). + * + * The declaration of a node can be recreated at any time when it is used. Caching it here is + * just a bit more efficient when it is used a lot. To make sure that the cache is up-to-date, + * call #nodeDeclarationEnsure before using it. + * + * Currently, the declaration is the same for every node of the same type. Going forward, that is + * intended to change though. Especially when nodes become more dynamic with respect to how many + * sockets they have. + */ + NodeDeclarationHandle *declaration = nullptr; + + /** #eNodeTreeChangedFlag. */ + uint32_t changed_flag = 0; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0e976f04dd1..c39ab22ce3a 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -180,6 +180,7 @@ struct Paint *BKE_paint_get_active_from_context(const struct bContext *C); ePaintMode BKE_paintmode_get_active_from_context(const struct bContext *C); ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref); struct Brush *BKE_paint_brush(struct Paint *paint); +const struct Brush *BKE_paint_brush_for_read(const struct Paint *p); void BKE_paint_brush_set(struct Paint *paint, struct Brush *br); struct Palette *BKE_paint_palette(struct Paint *paint); void BKE_paint_palette_set(struct Paint *p, struct Palette *palette); @@ -733,7 +734,7 @@ enum { /* paint_vertex.cc */ /** - * Fills the object's active color atribute layer with the fill color. + * Fills the object's active color attribute layer with the fill color. * * \param[in] ob: The object. * \param[in] fill_color: The fill color. diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 123fdbb8bac..5f8b2fafdd3 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -583,7 +583,7 @@ void psys_interpolate_face(struct Mesh *mesh, const float (*vert_normals)[3], struct MFace *mface, struct MTFace *tface, - float (*orcodata)[3], + const float (*orcodata)[3], float w[4], float vec[3], float nor[3], diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh index fdfb5fa037e..e73950e6299 100644 --- a/source/blender/blenkernel/BKE_pbvh_pixels.hh +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -132,12 +132,22 @@ struct UDIMTilePixels { } }; +struct UDIMTileUndo { + short tile_number; + rcti region; + + UDIMTileUndo(short tile_number, rcti ®ion) : tile_number(tile_number), region(region) + { + } +}; + struct NodeData { struct { bool dirty : 1; } flags; Vector<UDIMTilePixels> tiles; + Vector<UDIMTileUndo> undo_regions; Triangles triangles; NodeData() @@ -155,6 +165,23 @@ struct NodeData { return nullptr; } + void rebuild_undo_regions() + { + undo_regions.clear(); + for (UDIMTilePixels &tile : tiles) { + rcti region; + BLI_rcti_init_minmax(®ion); + for (PackedPixelRow &pixel_row : tile.pixel_rows) { + BLI_rcti_do_minmax_v( + ®ion, int2(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y)); + BLI_rcti_do_minmax_v(®ion, + int2(pixel_row.start_image_coordinate.x + pixel_row.num_pixels + 1, + pixel_row.start_image_coordinate.y + 1)); + } + undo_regions.append(UDIMTileUndo(tile.tile_number, region)); + } + } + void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer) { UDIMTilePixels *tile = find_tile_data(image_tile); diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h index 2144419728e..3e06bd84805 100644 --- a/source/blender/blenkernel/BKE_shrinkwrap.h +++ b/source/blender/blenkernel/BKE_shrinkwrap.h @@ -73,7 +73,7 @@ typedef struct ShrinkwrapTreeData { BVHTreeFromMesh treeData; const float (*pnors)[3]; - float (*clnors)[3]; + const float (*clnors)[3]; ShrinkwrapBoundaryData *boundary; } ShrinkwrapTreeData; diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 8931418e49c..edb301f06bf 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -20,6 +20,7 @@ struct Depsgraph; struct Main; struct Sequence; struct bSound; +struct SoundInfo; typedef struct SoundWaveform { int length; @@ -78,6 +79,7 @@ typedef enum eSoundChannels { typedef struct SoundInfo { struct { eSoundChannels channels; + int samplerate; } specs; float length; } SoundInfo; diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 6cbb47dc709..28f326a4ad4 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -102,7 +102,7 @@ class Spline { /** Return the number of control points. */ virtual int size() const = 0; - int segments_size() const; + int segments_num() const; bool is_cyclic() const; void set_cyclic(bool value); @@ -127,8 +127,8 @@ class Spline { * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. */ virtual void mark_cache_invalid() = 0; - virtual int evaluated_points_size() const = 0; - int evaluated_edges_size() const; + virtual int evaluated_points_num() const = 0; + int evaluated_edges_num() const; float length() const; @@ -164,7 +164,7 @@ class Spline { /** * The index of the evaluated point after the result location, accounting for wrapping when * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will - * be the last index (#evaluated_points_size - 1). + * be the last index (#evaluated_points_num - 1). */ int next_evaluated_index; /** @@ -191,7 +191,7 @@ class Spline { * indices and factors to the next index encoded in floats. The logic for converting from the * float values to interpolation data is in #lookup_data_from_index_factor. */ - blender::Array<float> sample_uniform_index_factors(int samples_size) const; + blender::Array<float> sample_uniform_index_factors(int samples_num) const; LookupResult lookup_data_from_index_factor(float index_factor) const; /** @@ -344,7 +344,7 @@ class BezierSpline final : public Spline { bool point_is_sharp(int index) const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; /** * Returns access to a cache of offsets into the evaluated point array for each control point. @@ -472,7 +472,7 @@ class NURBSpline final : public Spline { /** * Determines where and how the control points affect the evaluated points. The length should - * always be the value returned by #knots_size(), and each value should be greater than or equal + * always be the value returned by #knots_num(), and each value should be greater than or equal * to the previous. Only invalidated when a point is added or removed. */ mutable blender::Vector<float> knots_; @@ -514,8 +514,8 @@ class NURBSpline final : public Spline { uint8_t order() const; void set_order(uint8_t value); - bool check_valid_size_and_order() const; - int knots_size() const; + bool check_valid_num_and_order() const; + int knots_num() const; void resize(int size) final; blender::MutableSpan<blender::float3> positions() final; @@ -530,7 +530,7 @@ class NURBSpline final : public Spline { blender::Span<float> weights() const; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -582,7 +582,7 @@ class PolySpline final : public Spline { blender::Span<float> tilts() const final; void mark_cache_invalid() final; - int evaluated_points_size() const final; + int evaluated_points_num() const final; blender::Span<blender::float3> evaluated_positions() const final; @@ -665,7 +665,7 @@ struct CurveEval { blender::Array<float> accumulated_spline_lengths() const; float total_length() const; - int total_control_point_size() const; + int total_control_point_num() const; void mark_cache_invalid(); diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index 436853fe47b..486c9430279 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -186,13 +186,17 @@ typedef struct Subdiv { } cache_; } Subdiv; -/* =================----====--===== MODULE ==========================------== */ +/* -------------------------------------------------------------------- + * Module. + */ /* (De)initialize the entire subdivision surface module. */ void BKE_subdiv_init(void); void BKE_subdiv_exit(void); -/* ========================== CONVERSION HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Conversion helpers. + */ /* NOTE: uv_smooth is eSubsurfUVSmooth. */ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int uv_smooth); @@ -200,7 +204,9 @@ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int eSubdivVtxBoundaryInterpolation BKE_subdiv_vtx_boundary_interpolation_from_subsurf( int boundary_smooth); -/* =============================== STATISTICS =============================== */ +/* -------------------------------------------------------------------- + * Statistics. + */ void BKE_subdiv_stats_init(SubdivStats *stats); @@ -211,11 +217,15 @@ void BKE_subdiv_stats_reset(SubdivStats *stats, eSubdivStatsValue value); void BKE_subdiv_stats_print(const SubdivStats *stats); -/* ================================ SETTINGS ================================ */ +/* -------------------------------------------------------------------- + * Settings. + */ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSettings *settings_b); -/* ============================== CONSTRUCTION ============================== */ +/* -------------------------------------------------------------------- + * Construction. + */ /* Construct new subdivision surface descriptor, from scratch, using given * settings and topology. */ @@ -240,7 +250,9 @@ Subdiv *BKE_subdiv_update_from_mesh(Subdiv *subdiv, void BKE_subdiv_free(Subdiv *subdiv); -/* ============================ DISPLACEMENT API ============================ */ +/* -------------------------------------------------------------------- + * Displacement API. + */ void BKE_subdiv_displacement_attach_from_multires(Subdiv *subdiv, struct Mesh *mesh, @@ -248,14 +260,18 @@ void BKE_subdiv_displacement_attach_from_multires(Subdiv *subdiv, void BKE_subdiv_displacement_detach(Subdiv *subdiv); -/* ============================ TOPOLOGY HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Topology helpers. + */ /* For each element in the array, this stores the total number of ptex faces up to that element, * with the total number of ptex faces being the last element in the array. The array is of length * `base face count + 1`. */ int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv); -/* =========================== PTEX FACES AND GRIDS ========================= */ +/* -------------------------------------------------------------------- + * PTex faces and grids. + */ /* For a given (ptex_u, ptex_v) within a ptex face get corresponding * (grid_u, grid_v) within a grid. */ diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 31a1912bc68..b30b707759c 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -8,7 +8,6 @@ #pragma once #include "BKE_DerivedMesh.h" -#include "BKE_customdata.h" #include "BLI_bitmap.h" #include "BLI_sys_types.h" @@ -21,6 +20,8 @@ struct CCGFace; struct CCGKey; struct DMFlagMat; struct Mesh; +struct MPoly; +struct MLoop; struct Subdiv; /* -------------------------------------------------------------------- @@ -309,8 +310,8 @@ typedef enum SubdivCCGAdjacencyType { * adjacent to a vertex, r_v1 and r_v2 will be the index of that vertex. */ SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg, const SubdivCCGCoord *coord, - const MLoop *mloop, - const MPoly *mpoly, + const struct MLoop *mloop, + const struct MPoly *mpoly, int *r_v1, int *r_v2); diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h index 7673f18317a..cb0f2ac7e32 100644 --- a/source/blender/blenkernel/BKE_subdiv_eval.h +++ b/source/blender/blenkernel/BKE_subdiv_eval.h @@ -62,7 +62,7 @@ void BKE_subdiv_eval_limit_point_and_derivatives(struct Subdiv *subdiv, void BKE_subdiv_eval_limit_point_and_normal( struct Subdiv *subdiv, int ptex_face_index, float u, float v, float r_P[3], float r_N[3]); -/* Evaluate smoothly interpolated vertex data (such as orco). */ +/* Evaluate smoothly interpolated vertex data (such as ORCO). */ void BKE_subdiv_eval_vertex_data(struct Subdiv *subdiv, const int ptex_face_index, const float u, diff --git a/source/blender/blenkernel/BKE_subsurf.h b/source/blender/blenkernel/BKE_subsurf.h index 5dd2935ad0d..f32a78c3015 100644 --- a/source/blender/blenkernel/BKE_subsurf.h +++ b/source/blender/blenkernel/BKE_subsurf.h @@ -61,27 +61,6 @@ int BKE_ccg_gridsize(int level); */ int BKE_ccg_factor(int low_level, int high_level); -/** - * Translate #GridHidden into the #ME_HIDE flag for MVerts. Assumes - * vertices are in the order output by #ccgDM_copyFinalVertArray. - */ -void subsurf_copy_grid_hidden(struct DerivedMesh *dm, - const struct MPoly *mpoly, - struct MVert *mvert, - const struct MDisps *mdisps); - -/** - * Translate #GridPaintMask into vertex paint masks. Assumes vertices - * are in the order output by #ccgDM_copyFinalVertArray. - */ -void subsurf_copy_grid_paint_mask(struct DerivedMesh *dm, - const struct MPoly *mpoly, - float *paint_mask, - const struct GridPaintMask *grid_paint_mask); - -bool subsurf_has_edges(struct DerivedMesh *dm); -bool subsurf_has_faces(struct DerivedMesh *dm); - typedef enum MultiresModifiedFlags { /* indicates the grids have been sculpted on, so MDisps * have to be updated */ diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 29eb180a2ab..3f6d32e2f70 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -28,7 +28,22 @@ struct Scene; struct bGPDlayer; struct rcti; -/* **** Common functions **** */ +/* -------------------------------------------------------------------- + * Common types and constants. + */ + +typedef enum eTrackArea { + TRACK_AREA_POINT = (1 << 0), + TRACK_AREA_PAT = (1 << 1), + TRACK_AREA_SEARCH = (1 << 2), + + TRACK_AREA_NONE = 0, + TRACK_AREA_ALL = (TRACK_AREA_POINT | TRACK_AREA_PAT | TRACK_AREA_SEARCH), +} eTrackArea; + +/* -------------------------------------------------------------------- + * Common functions. + */ /** * Free tracking structure, only frees structure contents @@ -84,7 +99,10 @@ void BKE_tracking_get_projection_matrix(struct MovieTracking *tracking, int winy, float mat[4][4]); -/* **** Clipboard **** */ +/* -------------------------------------------------------------------- + * Clipboard. + */ + /** * Free clipboard by freeing memory used by all tracks in it. */ @@ -204,15 +222,21 @@ bool BKE_tracking_track_has_marker_at_frame(struct MovieTrackingTrack *track, in bool BKE_tracking_track_has_enabled_marker_at_frame(struct MovieTrackingTrack *track, int framenr); /** - * Clear track's path: - * - * - If action is #TRACK_CLEAR_REMAINED path from `ref_frame+1` up to end will be clear. - * - If action is #TRACK_CLEAR_UPTO path from the beginning up to `ref_frame-1` will be clear. - * - If action is #TRACK_CLEAR_ALL only marker at frame ref_frame will remain. + * Clear track's path. * * \note frame number should be in clip space, not scene space. */ -void BKE_tracking_track_path_clear(struct MovieTrackingTrack *track, int ref_frame, int action); +typedef enum eTrackClearAction { + /* Clear path from `ref_frame+1` up to the . */ + TRACK_CLEAR_UPTO, + /* Clear path from the beginning up to `ref_frame-1`. */ + TRACK_CLEAR_REMAINED, + /* Only marker at frame `ref_frame` will remain. */ + TRACK_CLEAR_ALL, +} eTrackClearAction; +void BKE_tracking_track_path_clear(struct MovieTrackingTrack *track, + int ref_frame, + eTrackClearAction action); void BKE_tracking_tracks_join(struct MovieTracking *tracking, struct MovieTrackingTrack *dst_track, @@ -240,7 +264,9 @@ float BKE_tracking_track_get_weight_for_marker(struct MovieClip *clip, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker); -/* Selection */ +/* -------------------------------------------------------------------- + * Selection. + */ /** * \param area: which part of marker should be selected. see TRACK_AREA_* constants. @@ -252,12 +278,43 @@ void BKE_tracking_track_select(struct ListBase *tracksbase, void BKE_tracking_track_deselect(struct MovieTrackingTrack *track, int area); void BKE_tracking_tracks_deselect_all(struct ListBase *tracksbase); -/* **** Marker **** */ +/* -------------------------------------------------------------------- + * Marker. + */ + struct MovieTrackingMarker *BKE_tracking_marker_insert(struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker); void BKE_tracking_marker_delete(struct MovieTrackingTrack *track, int framenr); -void BKE_tracking_marker_clamp(struct MovieTrackingMarker *marker, int event); +/** + * If the pattern area is outside of the search area its position will be modified in a way that it + * is within the pattern is within the search area. + * + * If the pattern area is already within the search area nothing happens. + * + * If the pattern area is bigger than the search area the behavior is undefined. + * + * Search area is never modified. + */ +void BKE_tracking_marker_clamp_pattern_position(struct MovieTrackingMarker *marker); + +/** + * If the search size is such that pattern area is (partially) outside of the search area make the + * search area bigger so that the pattern is within the search area. + * + * Pattern area is never modified. + */ +void BKE_tracking_marker_clamp_search_size(struct MovieTrackingMarker *marker); + +/** + * If the search position is such that pattern area is (partially) outside of the search area move + * the search area so that the pattern is within the search area. + * + * If the search area is smaller than the pattern the behavior is undefined. + * + * Pattern area is never modified. + */ +void BKE_tracking_marker_clamp_search_position(struct MovieTrackingMarker *marker); /** * Get marker closest to the given frame number. @@ -296,7 +353,10 @@ void BKE_tracking_marker_get_subframe_position(struct MovieTrackingTrack *track, float framenr, float pos[2]); -/* **** Plane Track **** */ +/* -------------------------------------------------------------------- + * Plane track. + */ + /** * Creates new plane track out of selected point tracks. */ @@ -342,7 +402,10 @@ void BKE_tracking_plane_tracks_replace_point_track(struct MovieTracking *trackin struct MovieTrackingTrack *old_track, struct MovieTrackingTrack *new_track); -/* **** Plane Marker **** */ +/* -------------------------------------------------------------------- + * Plane marker. + */ + struct MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert( struct MovieTrackingPlaneTrack *plane_track, struct MovieTrackingPlaneMarker *plane_marker); void BKE_tracking_plane_marker_delete(struct MovieTrackingPlaneTrack *plane_track, int framenr); @@ -368,7 +431,10 @@ void BKE_tracking_plane_marker_get_subframe_corners(struct MovieTrackingPlaneTra float framenr, float corners[4][2]); -/* **** Object **** */ +/* -------------------------------------------------------------------- + * Object. + */ + struct MovieTrackingObject *BKE_tracking_object_add(struct MovieTracking *tracking, const char *name); bool BKE_tracking_object_delete(struct MovieTracking *tracking, @@ -390,7 +456,10 @@ struct ListBase *BKE_tracking_object_get_plane_tracks(struct MovieTracking *trac struct MovieTrackingReconstruction *BKE_tracking_object_get_reconstruction( struct MovieTracking *tracking, struct MovieTrackingObject *object); -/* **** Camera **** */ +/* -------------------------------------------------------------------- + * Camera. + */ + /** * Converts principal offset from center to offset of blender's camera. */ @@ -409,7 +478,10 @@ void BKE_tracking_camera_get_reconstructed_interpolate(struct MovieTracking *tra float framenr, float mat[4][4]); -/* **** Distortion/Undistortion **** */ +/* -------------------------------------------------------------------- + * (Un)distortion. + */ + struct MovieDistortion *BKE_tracking_distortion_new(struct MovieTracking *tracking, int calibration_width, int calibration_height); @@ -463,7 +535,10 @@ void BKE_tracking_max_distortion_delta_across_bound(struct MovieTracking *tracki bool undistort, float delta[2]); -/* **** Image sampling **** */ +/* -------------------------------------------------------------------- + * Image sampling. + */ + struct ImBuf *BKE_tracking_sample_pattern(int frame_width, int frame_height, struct ImBuf *search_ib, @@ -493,7 +568,9 @@ struct ImBuf *BKE_tracking_get_search_imbuf(struct ImBuf *ibuf, void BKE_tracking_disable_channels( struct ImBuf *ibuf, bool disable_red, bool disable_green, bool disable_blue, bool grayscale); -/* **** 2D tracking **** */ +/* -------------------------------------------------------------------- + * 2D tracking. + */ /** * Refine marker's position using previously known keyframe. @@ -505,7 +582,9 @@ void BKE_tracking_refine_marker(struct MovieClip *clip, struct MovieTrackingMarker *marker, bool backwards); -/* *** 2D auto track *** */ +/* -------------------------------------------------------------------- + * 2D tracking using auto-track pipeline. + */ struct AutoTrackContext *BKE_autotrack_context_new(struct MovieClip *clip, struct MovieClipUser *user, @@ -517,7 +596,9 @@ void BKE_autotrack_context_sync_user(struct AutoTrackContext *context, struct Mo void BKE_autotrack_context_finish(struct AutoTrackContext *context); void BKE_autotrack_context_free(struct AutoTrackContext *context); -/* **** Plane tracking **** */ +/* -------------------------------------------------------------------- + * Plane tracking. + */ /** * \note frame number should be in clip space, not scene space. @@ -530,7 +611,9 @@ void BKE_tracking_homography_between_two_quads(/*const*/ float reference_corners /*const*/ float corners[4][2], float H[3][3]); -/* **** Camera solving **** */ +/* -------------------------------------------------------------------- + * Camera solving. + */ /** * Perform early check on whether everything is fine to start reconstruction. @@ -617,7 +700,9 @@ void BKE_tracking_detect_harris(struct MovieTracking *tracking, struct bGPDlayer *layer, bool place_outside_layer); -/* **** 2D stabilization **** */ +/* -------------------------------------------------------------------- + * 2D stabilization. + */ /** * Get stabilization data (translation, scaling and angle) for a given frame. @@ -686,7 +771,9 @@ void BKE_tracking_dopesheet_tag_update(struct MovieTracking *tracking); */ void BKE_tracking_dopesheet_update(struct MovieTracking *tracking); -/* **** Query/search **** */ +/* -------------------------------------------------------------------- + * Query and search. + */ /** * \note Returns NULL if the track comes from camera object,. @@ -722,7 +809,9 @@ void BKE_tracking_get_rna_path_prefix_for_plane_track( char *rna_path, size_t rna_path_len); -/* **** Utility macros **** */ +/* -------------------------------------------------------------------- + * Utility macros. + */ #define TRACK_SELECTED(track) \ ((track)->flag & SELECT || (track)->pat_flag & SELECT || (track)->search_flag & SELECT) @@ -745,22 +834,6 @@ void BKE_tracking_get_rna_path_prefix_for_plane_track( (((marker)->flag & MARKER_DISABLED) == 0 || ((sc)->flag & SC_HIDE_DISABLED) == 0 || \ ((sc)->clip->tracking.act_track == track)) -#define TRACK_CLEAR_UPTO 0 -#define TRACK_CLEAR_REMAINED 1 -#define TRACK_CLEAR_ALL 2 - -#define CLAMP_PAT_DIM 1 -#define CLAMP_PAT_POS 2 -#define CLAMP_SEARCH_DIM 3 -#define CLAMP_SEARCH_POS 4 - -#define TRACK_AREA_NONE -1 -#define TRACK_AREA_POINT 1 -#define TRACK_AREA_PAT 2 -#define TRACK_AREA_SEARCH 4 - -#define TRACK_AREA_ALL (TRACK_AREA_POINT | TRACK_AREA_PAT | TRACK_AREA_SEARCH) - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a57d4d0a2bf..8dc6f711fae 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -76,7 +76,7 @@ set(SRC intern/asset_catalog_path.cc intern/asset_library.cc intern/asset_library_service.cc - intern/attribute.c + intern/attribute.cc intern/attribute_access.cc intern/attribute_math.cc intern/autoexec.c @@ -117,6 +117,7 @@ set(SRC intern/curveprofile.cc intern/curves.cc intern/curves_geometry.cc + intern/curves_utils.cc intern/customdata.cc intern/customdata_file.c intern/data_transfer.c @@ -173,7 +174,7 @@ set(SRC intern/lib_id_delete.c intern/lib_id_eval.c intern/lib_id_remapper.cc - intern/lib_override.c + intern/lib_override.cc intern/lib_override_proxy_conversion.c intern/lib_query.c intern/lib_remap.c @@ -242,8 +243,8 @@ set(SRC intern/particle_child.c intern/particle_distribute.c intern/particle_system.c - intern/pbvh.cc intern/pbvh.c + intern/pbvh.cc intern/pbvh_bmesh.c intern/pbvh_pixels.cc intern/pointcache.c @@ -356,6 +357,7 @@ set(SRC BKE_curveprofile.h BKE_curves.h BKE_curves.hh + BKE_curves_utils.hh BKE_customdata.h BKE_customdata_file.h BKE_data_transfer.h @@ -429,6 +431,7 @@ set(SRC BKE_multires.h BKE_nla.h BKE_node.h + BKE_node_runtime.hh BKE_node_tree_update.h BKE_object.h BKE_object_deform.h @@ -608,7 +611,7 @@ if(WITH_IMAGE_HDR) endif() if(WITH_IMAGE_WEBP) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() if(WITH_CODEC_AVI) diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 6b43fe57e93..5cf0ca6e062 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -423,21 +423,6 @@ static void mesh_set_only_copy(Mesh *mesh, const CustomData_MeshMasks *mask) #endif } -void DM_add_vert_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->vertData, type, alloctype, layer, dm->numVertData); -} - -void DM_add_edge_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->edgeData, type, alloctype, layer, dm->numEdgeData); -} - -void DM_add_poly_layer(DerivedMesh *dm, int type, eCDAllocType alloctype, void *layer) -{ - CustomData_add_layer(&dm->polyData, type, alloctype, layer, dm->numPolyData); -} - void *DM_get_vert_data_layer(DerivedMesh *dm, int type) { if (type == CD_MVERT) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index b886722676b..94b85e42f96 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -2868,7 +2868,7 @@ static void nlastrip_evaluate_meta(const int evaluation_mode, /* Assert currently supported modes. If new mode added, then assertion marks potentially missed * area. * - * Note: In the future if support is ever added to metastrips to support nested tracks, then + * NOTE: In the future if support is ever added to metastrips to support nested tracks, then * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only * case difference should be the evaluation order. diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 6ce6bee547c..636e0af0edf 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -41,7 +41,7 @@ static std::string get_new_internal_name() { static std::atomic<int> index = 0; const int next_index = index.fetch_add(1); - return "anonymous_attribute_" + std::to_string(next_index); + return ".a_" + std::to_string(next_index); } AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 8d3649fef08..031d3647878 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -61,7 +61,7 @@ static CLG_LogRef LOG = {"bke.appdir"}; static struct { /** Full path to program executable. */ - char program_filename[FILE_MAX]; + char program_filepath[FILE_MAX]; /** Full path to directory in which executable is located. */ char program_dirname[FILE_MAX]; /** Persistent temporary directory (defined by the preferences or OS). */ @@ -860,14 +860,14 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name) void BKE_appdir_program_path_init(const char *argv0) { - where_am_i(g_app.program_filename, sizeof(g_app.program_filename), argv0); - BLI_split_dir_part(g_app.program_filename, g_app.program_dirname, sizeof(g_app.program_dirname)); + where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0); + BLI_split_dir_part(g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname)); } const char *BKE_appdir_program_path(void) { - BLI_assert(g_app.program_filename[0]); - return g_app.program_filename; + BLI_assert(g_app.program_filepath[0]); + return g_app.program_filepath; } const char *BKE_appdir_program_dir(void) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index 11f36e32b74..81eb1786322 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -318,7 +318,7 @@ TEST_F(AssetCatalogTest, load_catalog_path_backslashes) const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); ASSERT_NE(nullptr, found_by_id); EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) - << "Backslashes should be normalised when loading from disk."; + << "Backslashes should be normalized when loading from disk."; EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.cc index 870920bdf76..7ca38573d25 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.cc @@ -7,7 +7,7 @@ * on top of CustomData, which manages individual domains. */ -#include <string.h> +#include <cstring> #include "MEM_guardedalloc.h" @@ -18,10 +18,12 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_index_range.hh" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" #include "BKE_attribute.h" +#include "BKE_attribute_access.hh" #include "BKE_curves.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" @@ -30,10 +32,12 @@ #include "RNA_access.h" -typedef struct DomainInfo { +using blender::IndexRange; + +struct DomainInfo { CustomData *customdata; int length; -} DomainInfo; +}; static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) { @@ -49,7 +53,7 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) case ID_ME: { Mesh *mesh = (Mesh *)id; BMEditMesh *em = mesh->edit_mesh; - if (em != NULL) { + if (em != nullptr) { BMesh *bm = em->bm; info[ATTR_DOMAIN_POINT].customdata = &bm->vdata; info[ATTR_DOMAIN_POINT].length = bm->totvert; @@ -75,9 +79,9 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) case ID_CV: { Curves *curves = (Curves *)id; info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data; - info[ATTR_DOMAIN_POINT].length = curves->geometry.point_size; + info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num; info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data; - info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_size; + info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num; break; } default: @@ -90,7 +94,7 @@ static CustomData *attribute_customdata_find(ID *id, CustomDataLayer *layer) DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata && ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { @@ -98,14 +102,14 @@ static CustomData *attribute_customdata_find(ID *id, CustomDataLayer *layer) } } - return NULL; + return nullptr; } -bool BKE_id_attributes_supported(struct ID *id) +bool BKE_id_attributes_supported(ID *id) { DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { if (info[domain].customdata) { return true; } @@ -113,6 +117,11 @@ bool BKE_id_attributes_supported(struct ID *id) return false; } +bool BKE_attribute_allow_procedural_access(const char *attribute_name) +{ + return blender::bke::allow_procedural_attribute_access(attribute_name); +} + bool BKE_id_attribute_rename(ID *id, CustomDataLayer *layer, const char *new_name, @@ -124,7 +133,7 @@ bool BKE_id_attribute_rename(ID *id, } CustomData *customdata = attribute_customdata_find(id, layer); - if (customdata == NULL) { + if (customdata == nullptr) { BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); return false; } @@ -134,9 +143,9 @@ bool BKE_id_attribute_rename(ID *id, return true; } -typedef struct AttrUniqueData { +struct AttrUniqueData { ID *id; -} AttrUniqueData; +}; static bool unique_name_cb(void *arg, const char *name) { @@ -145,7 +154,7 @@ static bool unique_name_cb(void *arg, const char *name) DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(data->id, info); - for (AttributeDomain domain = ATTR_DOMAIN_POINT; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { if (!info[domain].customdata) { continue; } @@ -165,11 +174,12 @@ static bool unique_name_cb(void *arg, const char *name) bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) { - AttrUniqueData data = {.id = id}; + AttrUniqueData data{id}; BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); - return BLI_uniquename_cb(unique_name_cb, &data, NULL, '.', outname, MAX_CUSTOMDATA_LAYER_NAME); + return BLI_uniquename_cb( + unique_name_cb, &data, nullptr, '.', outname, MAX_CUSTOMDATA_LAYER_NAME); } CustomDataLayer *BKE_id_attribute_new( @@ -179,9 +189,9 @@ CustomDataLayer *BKE_id_attribute_new( get_domains(id, info); CustomData *customdata = info[domain].customdata; - if (customdata == NULL) { + if (customdata == nullptr) { BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type"); - return NULL; + return nullptr; } char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; @@ -191,24 +201,24 @@ CustomDataLayer *BKE_id_attribute_new( case ID_ME: { Mesh *me = (Mesh *)id; BMEditMesh *em = me->edit_mesh; - if (em != NULL) { + if (em != nullptr) { BM_data_layer_add_named(em->bm, customdata, type, uniquename); } else { CustomData_add_layer_named( - customdata, type, CD_DEFAULT, NULL, info[domain].length, uniquename); + customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); } break; } default: { CustomData_add_layer_named( - customdata, type, CD_DEFAULT, NULL, info[domain].length, uniquename); + customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); break; } } const int index = CustomData_get_named_layer_index(customdata, type, uniquename); - return (index == -1) ? NULL : &(customdata->layers[index]); + return (index == -1) ? nullptr : &(customdata->layers[index]); } bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports) @@ -232,7 +242,7 @@ bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports case ID_ME: { Mesh *me = (Mesh *)id; BMEditMesh *em = me->edit_mesh; - if (em != NULL) { + if (em != nullptr) { BM_data_layer_free(em->bm, customdata, layer->type); } else { @@ -260,8 +270,8 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id, get_domains(id, info); CustomData *customdata = info[domain].customdata; - if (customdata == NULL) { - return NULL; + if (customdata == nullptr) { + return nullptr; } for (int i = 0; i < customdata->totlayer; i++) { @@ -271,7 +281,7 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id, } } - return NULL; + return nullptr; } CustomDataLayer *BKE_id_attribute_search(const ID *id, @@ -310,7 +320,7 @@ int BKE_id_attributes_length(const ID *id, AttributeDomainMask domain_mask, Cust int length = 0; - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata && ((1 << (int)domain) & domain_mask)) { @@ -326,16 +336,16 @@ AttributeDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *lay DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata && ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { - return domain; + return static_cast<AttributeDomain>(domain); } } BLI_assert_msg(0, "Custom data layer not found in geometry"); - return ATTR_DOMAIN_NUM; + return static_cast<AttributeDomain>(ATTR_DOMAIN_POINT); } int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) @@ -346,7 +356,7 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) switch (GS(id->name)) { case ID_ME: { Mesh *mesh = (Mesh *)id; - if (mesh->edit_mesh != NULL) { + if (mesh->edit_mesh != nullptr) { return 0; } } @@ -357,7 +367,7 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata && ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { @@ -395,7 +405,7 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id) int index = 0; - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata) { for (int i = 0; i < customdata->totlayer; i++) { @@ -410,7 +420,7 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id) } } - return NULL; + return nullptr; } void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer) @@ -420,7 +430,7 @@ void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer) int index = 0; - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata) { for (int i = 0; i < customdata->totlayer; i++) { @@ -450,7 +460,7 @@ int *BKE_id_attributes_active_index_p(ID *id) return &((Curves *)id)->attributes_active_index; } default: - return NULL; + return nullptr; } } @@ -459,9 +469,9 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - bool use_next = (layers == NULL); + bool use_next = (layers == nullptr); - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (customdata && customdata->layers && customdata->totlayer) { if (customdata->layers == layers) { @@ -473,7 +483,7 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye } } - return NULL; + return nullptr; } CustomDataLayer *BKE_id_attribute_from_index(ID *id, @@ -485,7 +495,7 @@ CustomDataLayer *BKE_id_attribute_from_index(ID *id, get_domains(id, info); int index = 0; - for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; if (!customdata || !((1 << (int)domain) & domain_mask)) { @@ -506,7 +516,7 @@ CustomDataLayer *BKE_id_attribute_from_index(ID *id, } } - return NULL; + return nullptr; } /** Get list of domain types but with ATTR_DOMAIN_FACE and @@ -514,15 +524,15 @@ CustomDataLayer *BKE_id_attribute_from_index(ID *id, */ static void get_domains_types(AttributeDomain domains[ATTR_DOMAIN_NUM]) { - for (AttributeDomain i = 0; i < ATTR_DOMAIN_NUM; i++) { - domains[i] = i; + for (const int i : IndexRange(ATTR_DOMAIN_NUM)) { + domains[i] = static_cast<AttributeDomain>(i); } /* Swap corner and face. */ SWAP(AttributeDomain, domains[ATTR_DOMAIN_FACE], domains[ATTR_DOMAIN_CORNER]); } -int BKE_id_attribute_to_index(const struct ID *id, +int BKE_id_attribute_to_index(const ID *id, const CustomDataLayer *layer, AttributeDomainMask domain_mask, CustomDataMask layer_mask) @@ -573,7 +583,7 @@ CustomDataLayer *BKE_id_attribute_subset_active_get(const ID *id, get_domains_types(domains); get_domains(id, info); - CustomDataLayer *candidate = NULL; + CustomDataLayer *candidate = nullptr; for (int i = 0; i < ARRAY_SIZE(domains); i++) { if (!((1 << domains[i]) & domain_mask) || !info[domains[i]].customdata) { continue; @@ -661,13 +671,13 @@ void BKE_id_attributes_render_color_set(ID *id, CustomDataLayer *active_layer) CustomDataLayer *BKE_id_attributes_color_find(const ID *id, const char *name) { CustomDataLayer *layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT); - if (layer == NULL) { + if (layer == nullptr) { layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER); } - if (layer == NULL) { + if (layer == nullptr) { layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_POINT); } - if (layer == NULL) { + if (layer == nullptr) { layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_CORNER); } @@ -691,7 +701,7 @@ void BKE_id_attribute_copy_domains_temp(short id_type, Mesh *me = (Mesh *)r_id; memset((void *)me, 0, sizeof(*me)); - me->edit_mesh = NULL; + me->edit_mesh = nullptr; me->vdata = vdata ? *vdata : reset; me->edata = edata ? *edata : reset; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d33b64c493b..e487bf4acf6 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -55,6 +55,14 @@ std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_i return stream; } +const char *no_procedural_access_message = + "This attribute can not be accessed in a procedural context"; + +bool allow_procedural_attribute_access(StringRef attribute_name) +{ + return !attribute_name.startswith(".selection"); +} + static int attribute_data_type_complexity(const CustomDataType data_type) { switch (data_type) { @@ -184,16 +192,16 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); return data != nullptr; } case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num); if (data == nullptr) { return false; } @@ -204,7 +212,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = CustomData_add_layer( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + &custom_data, data_type, CD_ASSIGN, source_data, domain_num); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -221,35 +229,35 @@ static void *add_generic_custom_data_layer(CustomData &custom_data, const CustomDataType data_type, const eCDAllocType alloctype, void *layer_data, - const int domain_size, + const int domain_num, const AttributeIDRef &attribute_id) { if (attribute_id.is_named()) { char attribute_name_c[MAX_NAME]; attribute_id.name().copy(attribute_name_c); return CustomData_add_layer_named( - &custom_data, data_type, alloctype, layer_data, domain_size, attribute_name_c); + &custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c); } const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); return CustomData_add_layer_anonymous( - &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); + &custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id); } static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, CustomData &custom_data, const CustomDataType data_type, - const int domain_size, + const int domain_num, const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); return data != nullptr; } case AttributeInit::Type::VArray: { void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); + custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id); if (data == nullptr) { return false; } @@ -260,7 +268,7 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr case AttributeInit::Type::MoveArray: { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; void *data = add_generic_custom_data_layer( - custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); + custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -303,8 +311,8 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(data, domain_size); + const int domain_num = component.attribute_domain_num(domain_); + return as_read_attribute_(data, domain_num); } WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( @@ -317,7 +325,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data; if (stored_as_named_attribute_) { @@ -333,10 +341,10 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_size); + custom_data, stored_type_, name_.c_str(), domain_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); } if (data != new_data) { @@ -353,7 +361,7 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( }; } - return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -366,7 +374,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); int layer_index; if (stored_as_named_attribute_) { for (const int i : IndexRange(custom_data->totlayer)) { @@ -381,7 +389,7 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const bool delete_success = CustomData_free_layer( - custom_data, stored_type_, domain_size, layer_index); + custom_data, stored_type_, domain_num, layer_index); if (delete_success) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); @@ -401,7 +409,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -409,7 +417,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_size, initializer); + name_, *custom_data, stored_type_, domain_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -417,7 +425,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_size, initializer); + *custom_data, stored_type_, domain_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { @@ -446,7 +454,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; @@ -455,7 +463,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_size}; + GSpan data{*type, layer.data, domain_num}; return {GVArray::ForSpan(data), domain_}; } return {}; @@ -468,24 +476,23 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( if (custom_data == nullptr) { return {}; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, domain_size); + CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_num); } else { CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + custom_data, layer.type, &attribute_id.anonymous_id(), domain_num); } const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); if (type == nullptr) { continue; } - GMutableSpan data{*type, layer.data, domain_size}; + GMutableSpan data{*type, layer.data, domain_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; @@ -498,12 +505,12 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, if (custom_data == nullptr) { return false; } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (this->type_is_supported((CustomDataType)layer.type) && custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_size, i); + CustomData_free_layer(custom_data, layer.type, domain_num, i); return true; } } @@ -531,9 +538,9 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_size, initializer); + attribute_id, *custom_data, data_type, domain_num, initializer); return true; } @@ -567,8 +574,8 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - return {as_read_attribute_(layer.data, domain_size), domain_}; + const int domain_num = component.attribute_domain_num(domain_); + return {as_read_attribute_(layer.data, domain_num), domain_}; } } } @@ -585,16 +592,16 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); + const int domain_num = component.attribute_domain_num(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_size); + custom_data, stored_type_, layer.name, domain_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } } - return {as_write_attribute_(layer.data, domain_size), domain_}; + return {as_write_attribute_(layer.data, domain_num), domain_}; } } } @@ -612,8 +619,8 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_size = component.attribute_domain_size(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_size, i); + const int domain_num = component.attribute_domain_num(domain_); + CustomData_free_layer(custom_data, stored_type_, domain_num, i); if (custom_data_access_.update_custom_data_pointers) { custom_data_access_.update_custom_data_pointers(component); } @@ -702,9 +709,9 @@ GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { - const int domain_size = this->size_; + const int domain_num = this->size_; return GVArray::ForSingle( - *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value); + *type, domain_num, (default_value == nullptr) ? type->default_value() : default_value); } if (attribute->type() == *type) { @@ -822,7 +829,7 @@ bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) return providers->supported_domains().contains(domain); } -int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const +int GeometryComponent::attribute_domain_num(const AttributeDomain UNUSED(domain)) const { return 0; } @@ -1157,8 +1164,8 @@ blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef if (default_value == nullptr) { default_value = type->default_value(); } - const int domain_size = this->attribute_domain_size(domain); - return blender::GVArray::ForSingle(*type, domain_size, default_value); + const int domain_num = this->attribute_domain_num(domain); + return blender::GVArray::ForSingle(*type, domain_num, default_value); } class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { @@ -1267,10 +1274,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); component.attribute_try_create_builtin( attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); @@ -1301,7 +1308,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, ignore_old_values); } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { @@ -1310,7 +1317,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, attribute_id, domain, data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_size, default_value))); + AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); } else { component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); @@ -1333,8 +1340,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing * attribute after processing is done. */ - void *data = MEM_mallocN_aligned( - cpp_type->size() * domain_size, cpp_type->alignment(), __func__); + void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); if (ignore_old_values) { /* This does nothing for trivially constructible types, but is necessary for correctness. */ cpp_type->default_construct_n(data, domain); @@ -1343,10 +1349,10 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Fill the temporary array with values from the existing attribute. */ GVArray old_varray = component.attribute_get_for_read( attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_size), data); + old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); } GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); + GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } @@ -1429,7 +1435,7 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c const StringRef name = get_random_id_attribute_name(domain); GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_size(domain)); + BLI_assert(attribute.size() == component.attribute_domain_num(domain)); return attribute; } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 8c021ed0e21..f0f47cb7a11 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -172,8 +172,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -207,14 +207,14 @@ 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) +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_num) { - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_num)); } -template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_num) { - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_num)); } /** @@ -226,8 +226,8 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_size); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_size); + using AsReadAttribute = GVArray (*)(const void *data, int domain_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index f93b3efa8dd..4b507beb6b3 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -96,7 +96,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, ReportList *reports, const uint64_t id_types_mask) { - /* Note: No recursive append here (no `BLO_LIBLINK_APPEND_RECURSIVE`), external linked data + /* NOTE: No recursive append here (no `BLO_LIBLINK_APPEND_RECURSIVE`), external linked data * should remain linked. */ const int flag = 0; const int id_tag_extra = 0; @@ -132,7 +132,7 @@ int BKE_copybuffer_paste(bContext *C, View3D *v3d = CTX_wm_view3d(C); /* may be NULL. */ const int id_tag_extra = 0; - /* Note: No recursive append here, external linked data should remain linked. */ + /* NOTE: No recursive append here, external linked data should remain linked. */ BLI_assert((flag & BLO_LIBLINK_APPEND_RECURSIVE) == 0); struct LibraryLink_Params liblink_params; diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index c32941ccbc9..a5096b4f9eb 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -65,7 +65,7 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu, if (UNDO_DISK) { const struct BlendFileReadParams params = {0}; BlendFileReadReport bf_reports = {.reports = NULL}; - struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, ¶ms, &bf_reports); + struct BlendFileData *bfd = BKE_blendfile_read(mfu->filepath, ¶ms, &bf_reports); if (bfd != NULL) { BKE_blendfile_read_setup(C, bfd, ¶ms, &bf_reports); success = true; @@ -108,20 +108,20 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) /* disk save version */ if (UNDO_DISK) { static int counter = 0; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; char numstr[32]; - /* Calculate current filename. */ + /* Calculate current filepath. */ counter++; counter = counter % U.undosteps; BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); - BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr); + BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), numstr); /* success = */ /* UNUSED */ BLO_write_file( - bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL); + bmain, filepath, fileflags, &(const struct BlendFileWriteParams){0}, NULL); - BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename)); + BLI_strncpy(mfu->filepath, filepath, sizeof(mfu->filepath)); } else { MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL; diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 62196ea63bd..ebf48acde0f 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -400,7 +400,7 @@ typedef struct LooseDataInstantiateContext { static bool object_in_any_scene(Main *bmain, Object *ob) { LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { - /* #BKE_scene_has_object checks bases cache of the scenes' viewlayer, not actual content of + /* #BKE_scene_has_object checks bases cache of the scenes' view-layer, not actual content of * their collections. */ if (BKE_collection_has_object_recursive(sce->master_collection, ob)) { return true; @@ -1114,7 +1114,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList * &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } if (local_appended_new_id != NULL) { @@ -1537,7 +1537,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, } if (GS(old_id->name) == ID_KE) { - /* Shape Keys are handled as part of their owning obdata (see below). This implies thar + /* Shape Keys are handled as part of their owning obdata (see below). This implies that * there is no way to know when the old pointer gets invalid, so just clear it immediately. */ item->userdata = NULL; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 3f243d04965..083d5af063a 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1559,6 +1559,7 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush) } BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; settings->add_amount = 1; + settings->points_per_curve = 8; settings->minimum_length = 0.01f; settings->curve_length = 0.3f; } diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 37b0875db67..35c2039634a 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -967,31 +967,6 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(float epsilon, return tree; } -BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data, - const MVert *vert, - const MFace *face, - const int numFaces, - const BLI_bitmap *faces_mask, - int faces_num_active, - float epsilon, - int tree_type, - int axis) -{ - BVHTree *tree = nullptr; - tree = bvhtree_from_mesh_faces_create_tree( - epsilon, tree_type, axis, vert, face, numFaces, faces_mask, faces_num_active); - - bvhtree_balance(tree, false); - - if (data) { - /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_setup_data( - tree, BVHTREE_FROM_FACES, vert, nullptr, face, nullptr, nullptr, nullptr, data); - } - - return tree; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -1212,10 +1187,11 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, int looptri_no_hidden_len = 0; int looptri_iter = 0; - const MPoly *mp = mpoly; + int i_poly = 0; while (looptri_iter != looptri_len) { - int mp_totlooptri = mp->totloop - 2; - if (mp->flag & ME_HIDE) { + int mp_totlooptri = mpoly[i_poly].totloop - 2; + const MPoly &mp = mpoly[i_poly]; + if (mp.flag & ME_HIDE) { looptri_iter += mp_totlooptri; } else { @@ -1225,7 +1201,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, looptri_no_hidden_len++; } } - mp++; + i_poly++; } *r_looptri_active_len = looptri_no_hidden_len; diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 32925168437..6325251647b 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -66,14 +66,19 @@ static void camera_init_data(ID *id) * * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ -static void camera_copy_data(Main *UNUSED(bmain), - ID *id_dst, - const ID *id_src, - const int UNUSED(flag)) +static void camera_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) { Camera *cam_dst = (Camera *)id_dst; const Camera *cam_src = (const Camera *)id_src; - BLI_duplicatelist(&cam_dst->bg_images, &cam_src->bg_images); + + /* We never handle usercount here for own data. */ + const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; + + BLI_listbase_clear(&cam_dst->bg_images); + LISTBASE_FOREACH (CameraBGImage *, bgpic_src, &cam_src->bg_images) { + CameraBGImage *bgpic_dst = BKE_camera_background_image_copy(bgpic_src, flag_subdata); + BLI_addtail(&cam_dst->bg_images, bgpic_dst); + } } /** Free (or release) any data used by this camera (does not free the camera itself). */ @@ -125,6 +130,11 @@ static void camera_blend_read_data(BlendDataReader *reader, ID *id) LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) { bgpic->iuser.scene = NULL; + + /* If linking from a library, clear 'local' library override flag. */ + if (ID_IS_LINKED(ca)) { + bgpic->flag &= ~CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL; + } } } @@ -302,7 +312,7 @@ void BKE_camera_params_from_object(CameraParams *params, const Object *cam_ob) } void BKE_camera_params_from_view3d(CameraParams *params, - Depsgraph *depsgraph, + const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d) { @@ -1119,13 +1129,31 @@ CameraBGImage *BKE_camera_background_image_new(Camera *cam) bgpic->scale = 1.0f; bgpic->alpha = 0.5f; bgpic->iuser.flag |= IMA_ANIM_ALWAYS; - bgpic->flag |= CAM_BGIMG_FLAG_EXPANDED; + bgpic->flag |= CAM_BGIMG_FLAG_EXPANDED | CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL; BLI_addtail(&cam->bg_images, bgpic); return bgpic; } +CameraBGImage *BKE_camera_background_image_copy(CameraBGImage *bgpic_src, const int flag) +{ + CameraBGImage *bgpic_dst = MEM_dupallocN(bgpic_src); + + bgpic_dst->next = bgpic_dst->prev = NULL; + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id_us_plus((ID *)bgpic_dst->ima); + id_us_plus((ID *)bgpic_dst->clip); + } + + if ((flag & LIB_ID_COPY_NO_LIB_OVERRIDE_LOCAL_DATA_FLAG) == 0) { + bgpic_dst->flag |= CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL; + } + + return bgpic_dst; +} + void BKE_camera_background_image_remove(Camera *cam, CameraBGImage *bgpic) { BLI_remlink(&cam->bg_images, bgpic); diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 2d742a103af..93286751f92 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -65,18 +65,6 @@ static int cdDM_getNumEdges(DerivedMesh *dm) return dm->numEdgeData; } -static int cdDM_getNumTessFaces(DerivedMesh *dm) -{ - /* uncomment and add a breakpoint on the printf() - * to help debug tessfaces issues since BMESH merge. */ -#if 0 - if (dm->numTessFaceData == 0 && dm->numPolyData != 0) { - printf("%s: has no faces!\n"); - } -#endif - return dm->numTessFaceData; -} - static int cdDM_getNumLoops(DerivedMesh *dm) { return dm->numLoopData; @@ -173,7 +161,6 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->getNumVerts = cdDM_getNumVerts; dm->getNumEdges = cdDM_getNumEdges; - dm->getNumTessFaces = cdDM_getNumTessFaces; dm->getNumLoops = cdDM_getNumLoops; dm->getNumPolys = cdDM_getNumPolys; diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 0d6a0c045a5..ab9a27a3996 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -632,7 +632,7 @@ static void cloth_apply_vgroup(ClothModifierData *clmd, Mesh *mesh) verts->flags &= ~(CLOTH_VERT_FLAG_PINNED | CLOTH_VERT_FLAG_NOSELFCOLL | CLOTH_VERT_FLAG_NOOBJCOLL); - MDeformVert *dvert = CustomData_get(&mesh->vdata, i, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get(&mesh->vdata, i, CD_MDEFORMVERT); if (dvert) { for (int j = 0; j < dvert->totweight; j++) { if (dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_mass - 1)) { @@ -715,7 +715,7 @@ static bool cloth_from_object( int i = 0; MVert *mvert = NULL; ClothVertex *verts = NULL; - float(*shapekey_rest)[3] = NULL; + const float(*shapekey_rest)[3] = NULL; const float tnull[3] = {0, 0, 0}; /* If we have a clothObject, free it. */ @@ -1127,7 +1127,7 @@ static void cloth_update_springs(ClothModifierData *clmd) spring->lin_stiffness = (v1->bend_stiff + v2->bend_stiff) / 2.0f; } else if (spring->type == CLOTH_SPRING_TYPE_GOAL) { - /* Warning: Appending NEW goal springs does not work + /* WARNING: Appending NEW goal springs does not work * because implicit solver would need reset! */ /* Activate / Deactivate existing springs */ diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index c3d66d4463d..e4c46703f8a 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1158,6 +1158,80 @@ bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap) return false; } +void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + minimums[i] = curve_mapping->cm[i].mintable; + } +} + +void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping, + float dividers[CM_TOT]) +{ + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable); + } +} + +void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping, + float start_slopes[CM_TOT], + float end_slopes[CM_TOT]) +{ + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + for (int i = 0; i < CM_TOT; i++) { + const CurveMap *curve_map = &curve_mapping->cm[i]; + /* If extrapolation is not enabled, the slopes are horizontal. */ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + start_slopes[i] = 0.0f; + end_slopes[i] = 0.0f; + continue; + } + + if (curve_map->ext_in[0] != 0.0f) { + start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]); + } + else { + start_slopes[i] = 1e8f; + } + + if (curve_map->ext_out[0] != 0.0f) { + end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]); + } + else { + end_slopes[i] = 1e8f; + } + } +} + +bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index) +{ + if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) { + return false; + } + const CurveMap *curve_map = &curve_mapping->cm[index]; + if (curve_map->maxtable - curve_map->mintable != 1.0f) { + return false; + } + if (curve_map->ext_in[0] != curve_map->ext_in[1]) { + return false; + } + if (curve_map->ext_out[0] != curve_map->ext_out[1]) { + return false; + } + if (curve_map->totpoint != 2) { + return false; + } + if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) { + return false; + } + if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) { + return false; + } + return true; +} + void BKE_curvemapping_init(CurveMapping *cumap) { int a; diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 96389c44839..14e862c2377 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -13,8 +13,8 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" #include "BLI_linklist.h" -#include "BLI_math.h" #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc index 13695525616..5ba17f1761b 100644 --- a/source/blender/blenkernel/intern/curve_bezier.cc +++ b/source/blender/blenkernel/intern/curve_bezier.cc @@ -20,8 +20,8 @@ bool segment_is_vector(const Span<int8_t> handle_types_left, handle_types_left[segment_index + 1] == BEZIER_HANDLE_VECTOR; } -bool last_cylic_segment_is_vector(const Span<int8_t> handle_types_left, - const Span<int8_t> handle_types_right) +bool last_cyclic_segment_is_vector(const Span<int8_t> handle_types_left, + const Span<int8_t> handle_types_right) { return handle_types_right.last() == BEZIER_HANDLE_VECTOR && handle_types_left.first() == BEZIER_HANDLE_VECTOR; @@ -49,7 +49,8 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left, } if (cyclic) { - offset += last_cylic_segment_is_vector(handle_types_left, handle_types_right) ? 1 : resolution; + offset += last_cyclic_segment_is_vector(handle_types_left, handle_types_right) ? 1 : + resolution; } else { offset++; diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc index 2db183eea3e..4b2174c912c 100644 --- a/source/blender/blenkernel/intern/curve_catmull_rom.cc +++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc @@ -9,11 +9,11 @@ namespace blender::bke::curves::catmull_rom { -int calculate_evaluated_size(const int points_num, const bool cyclic, const int resolution) +int calculate_evaluated_num(const int points_num, const bool cyclic, const int resolution) { - const int eval_size = resolution * curve_segment_size(points_num, cyclic); + const int eval_num = resolution * curve_segment_num(points_num, cyclic); /* If the curve isn't cyclic, one last point is added to the final point. */ - return cyclic ? eval_size : eval_size + 1; + return cyclic ? eval_num : eval_num + 1; } /* Adapted from Cycles #catmull_rom_basis_eval function. */ @@ -46,7 +46,7 @@ static void interpolate_to_evaluated(const Span<T> src, MutableSpan<T> dst) { - BLI_assert(dst.size() == calculate_evaluated_size(src.size(), cyclic, resolution)); + BLI_assert(dst.size() == calculate_evaluated_num(src.size(), cyclic, resolution)); /* - First deal with one and two point curves need special attention. * - Then evaluate the first and last segment(s) whose control points need to wrap around diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 3d9dd3ecf31..dd2bd982506 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -117,7 +117,7 @@ float CurveEval::total_length() const return length; } -int CurveEval::total_control_point_size() const +int CurveEval::total_control_point_num() const { int count = 0; for (const SplinePtr &spline : this->splines()) { @@ -144,7 +144,7 @@ blender::Array<int> CurveEval::evaluated_point_offsets() const int offset = 0; for (const int i : splines_.index_range()) { offsets[i] = offset; - offset += splines_[i]->evaluated_points_size(); + offset += splines_[i]->evaluated_points_num(); } offsets.last() = offset; return offsets; @@ -373,15 +373,15 @@ static void copy_attributes_between_components(const GeometryComponent &src_comp }); } -std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) { CurveComponent src_component; - src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly); - const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( - curves.geometry); + src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly); + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( + curves_id.geometry); - VArray<int> resolution = geometry.resolution(); - VArray<int8_t> normal_mode = geometry.normal_mode(); + VArray<int> resolution = curves.resolution(); + VArray<int8_t> normal_mode = curves.normal_mode(); VArray_Span<float> nurbs_weights{ src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; @@ -396,34 +396,34 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) 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(); + VArray<int8_t> curve_types = curves.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.points_for_curve(curve_index); + const IndexRange points = curves.points_for_curve(curve_index); std::unique_ptr<Spline> spline; /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */ switch (std::max<int8_t>(1, curve_types[curve_index])) { case CURVE_TYPE_POLY: { spline = std::make_unique<PolySpline>(); - spline->resize(point_range.size()); + spline->resize(points.size()); break; } case CURVE_TYPE_BEZIER: { std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>(); - bezier_spline->resize(point_range.size()); + bezier_spline->resize(points.size()); bezier_spline->set_resolution(resolution[curve_index]); - 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)); + bezier_spline->handle_types_left().copy_from(handle_types_left.slice(points)); + bezier_spline->handle_types_right().copy_from(handle_types_right.slice(points)); 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->resize(points.size()); nurb_spline->set_resolution(resolution[curve_index]); - nurb_spline->weights().copy_from(nurbs_weights.slice(point_range)); + nurb_spline->weights().copy_from(nurbs_weights.slice(points)); nurb_spline->set_order(nurbs_orders[curve_index]); nurb_spline->knots_mode = static_cast<KnotsMode>(nurbs_knots_modes[curve_index]); @@ -463,14 +463,14 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) 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()); + Curves *curves_id = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(), + curve_eval.splines().size()); CurveComponent dst_component; - dst_component.replace(curves, GeometryOwnershipType::Editable); + dst_component.replace(curves_id, GeometryOwnershipType::Editable); - blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); - geometry.offsets_for_write().copy_from(curve_eval.control_point_offsets()); - MutableSpan<int8_t> curve_types = geometry.curve_types_for_write(); + blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry); + curves.offsets_for_write().copy_from(curve_eval.control_point_offsets()); + MutableSpan<int8_t> curve_types = curves.curve_types_for_write(); OutputAttribute_Typed<int8_t> normal_mode = dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); @@ -498,22 +498,22 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) const Spline &spline = *curve_eval.splines()[curve_index]; curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); normal_mode.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode; - const IndexRange point_range = geometry.points_for_curve(curve_index); + const IndexRange points = curves.points_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()); + handle_type_right.as_span().slice(points).copy_from(src.handle_types_right()); + handle_type_left.as_span().slice(points).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()); + nurbs_weight.as_span().slice(points).copy_from(src.weights()); break; } case CURVE_TYPE_CATMULL_ROM: { @@ -523,7 +523,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) } } - geometry.update_curve_types(); + curves.update_curve_types(); normal_mode.save(); nurbs_weight.save(); @@ -537,7 +537,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) copy_attributes_between_components(src_component, dst_component, {}); - return curves; + return curves_id; } void CurveEval::assert_valid_point_attributes() const diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 0114c0b45f4..cd6b64e9a03 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -10,10 +10,10 @@ namespace blender::bke::curves::nurbs { -bool check_valid_size_and_order(const int points_num, - const int8_t order, - const bool cyclic, - const KnotsMode knots_mode) +bool check_valid_num_and_order(const int points_num, + const int8_t order, + const bool cyclic, + const KnotsMode knots_mode) { if (points_num < order) { return false; @@ -29,19 +29,19 @@ bool check_valid_size_and_order(const int points_num, return true; } -int calculate_evaluated_size(const int points_num, - const int8_t order, - const bool cyclic, - const int resolution, - const KnotsMode knots_mode) +int calculate_evaluated_num(const int points_num, + const int8_t order, + const bool cyclic, + const int resolution, + const KnotsMode knots_mode) { - if (!check_valid_size_and_order(points_num, order, cyclic, knots_mode)) { - return 0; + if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { + return points_num; } - return resolution * curve_segment_size(points_num, cyclic); + return resolution * curve_segment_num(points_num, cyclic); } -int knots_size(const int points_num, const int8_t order, const bool cyclic) +int knots_num(const int points_num, const int8_t order, const bool cyclic) { if (cyclic) { return points_num + order * 2 - 1; @@ -55,7 +55,7 @@ void calculate_knots(const int points_num, const bool cyclic, MutableSpan<float> knots) { - BLI_assert(knots.size() == knots_size(points_num, order, cyclic)); + BLI_assert(knots.size() == knots_num(points_num, order, cyclic)); UNUSED_VARS_NDEBUG(points_num); const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); @@ -147,7 +147,7 @@ static void calculate_basis_for_point(const float parameter, } void calculate_basis_cache(const int points_num, - const int evaluated_size, + const int evaluated_num, const int8_t order, const bool cyclic, const Span<float> knots, @@ -157,10 +157,10 @@ void calculate_basis_cache(const int points_num, const int8_t degree = order - 1; - basis_cache.weights.resize(evaluated_size * order); - basis_cache.start_indices.resize(evaluated_size); + basis_cache.weights.resize(evaluated_num * order); + basis_cache.start_indices.resize(evaluated_num); - if (evaluated_size == 0) { + if (evaluated_num == 0) { return; } @@ -168,12 +168,12 @@ void calculate_basis_cache(const int points_num, MutableSpan<int> basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_size = curve_segment_size(evaluated_size, cyclic); + const int evaluated_segment_num = curve_segment_num(evaluated_num, cyclic); const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / evaluated_segment_size; - for (const int i : IndexRange(evaluated_size)) { + const float step = (end - start) / evaluated_segment_num; + for (const int i : IndexRange(evaluated_num)) { /* Clamp parameter due to floating point inaccuracy. */ const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); @@ -232,8 +232,12 @@ void interpolate_to_evaluated(const BasisCache &basis_cache, const GSpan src, GMutableSpan dst) { - BLI_assert(dst.size() == basis_cache.start_indices.size()); + if (basis_cache.invalid) { + dst.copy_from(src); + return; + } + BLI_assert(dst.size() == basis_cache.start_indices.size()); attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc index 2a546e81825..7ab92068d81 100644 --- a/source/blender/blenkernel/intern/curve_poly.cc +++ b/source/blender/blenkernel/intern/curve_poly.cc @@ -159,7 +159,7 @@ void calculate_normals_minimum(const Span<float3> tangents, normals.first() = math::normalize(float3(first_tangent.y, -first_tangent.x, 0.0f)); } - /* Forward normal with minimum twist along the entire spline. */ + /* Forward normal with minimum twist along the entire curve. */ for (const int i : IndexRange(1, normals.size() - 1)) { normals[i] = calculate_next_normal(normals[i - 1], tangents[i - 1], tangents[i]); } @@ -169,7 +169,7 @@ void calculate_normals_minimum(const Span<float3> tangents, } /* Compute how much the first normal deviates from the normal that has been forwarded along the - * entire cyclic spline. */ + * entire cyclic curve. */ const float3 uncorrected_last_normal = calculate_next_normal( normals.last(), tangents.last(), tangents.first()); float correction_angle = angle_signed_on_axis_v3v3_v3( diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index ef921797698..0cd324cfe2c 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -39,8 +39,8 @@ static void fill_mesh_topology(const int vert_offset, MutableSpan<MLoop> loops, MutableSpan<MPoly> polys) { - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); if (profile_point_num == 1) { for (const int i : IndexRange(main_point_num - 1)) { @@ -134,9 +134,9 @@ static void fill_mesh_topology(const int vert_offset, const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; if (has_caps) { - const int poly_size = main_segment_num * profile_segment_num; - const int cap_loop_offset = loop_offset + poly_size * 4; - const int cap_poly_offset = poly_offset + poly_size; + const int poly_num = main_segment_num * profile_segment_num; + const int cap_loop_offset = loop_offset + poly_num * 4; + const int cap_poly_offset = poly_offset + poly_num; MPoly &poly_start = polys[cap_poly_offset]; poly_start.loopstart = cap_loop_offset; @@ -273,7 +273,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool for (const int i_main : info.main.curves_range()) { const bool main_cyclic = info.main_cyclic[i_main]; const int main_point_num = info.main.evaluated_points_for_curve(i_main).size(); - const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic); + const int main_segment_num = curves::curve_segment_num(main_point_num, main_cyclic); for (const int i_profile : info.profile.curves_range()) { result.vert[mesh_index] = vert_offset; result.edge[mesh_index] = edge_offset; @@ -285,8 +285,7 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool const bool profile_cyclic = info.profile_cyclic[i_profile]; const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size(); - const int profile_segment_num = curves::curve_segment_size(profile_point_num, - profile_cyclic); + const int profile_segment_num = curves::curve_segment_num(profile_point_num, profile_cyclic); const bool has_caps = fill_caps && !main_cyclic && profile_cyclic; const int tube_face_num = main_segment_num * profile_segment_num; @@ -408,8 +407,8 @@ static void foreach_curve_combination(const CurvesInfo &info, profile_points, main_cyclic, profile_cyclic, - curves::curve_segment_size(main_points.size(), main_cyclic), - curves::curve_segment_size(profile_points.size(), profile_cyclic), + curves::curve_segment_num(main_points.size(), main_cyclic), + curves::curve_segment_num(profile_points.size(), profile_cyclic), offsets_to_range(offsets.vert.as_span(), i), offsets_to_range(offsets.edge.as_span(), i), offsets_to_range(offsets.poly.as_span(), i), diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 1df1492bac1..ab9dd702630 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -23,6 +23,7 @@ #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_anim_data.h" #include "BKE_curves.hh" @@ -48,6 +49,7 @@ using blender::IndexRange; using blender::MutableSpan; using blender::RandomNumberGenerator; using blender::Span; +using blender::Vector; static const char *ATTR_POSITION = "position"; @@ -78,12 +80,12 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, * 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; + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - 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); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_num); dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); @@ -121,12 +123,10 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre { Curves *curves = (Curves *)id; - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare( - &curves->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_blend_write_prepare( - &curves->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); + Vector<CustomDataLayer, 16> point_layers; + Vector<CustomDataLayer, 16> curve_layers; + CustomData_blend_write_prepare(curves->geometry.point_data, point_layers); + CustomData_blend_write_prepare(curves->geometry.curve_data, curve_layers); /* Write LibData */ BLO_write_id_struct(writer, Curves, id_address, &curves->id); @@ -135,31 +135,23 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre /* Direct data */ CustomData_blend_write(writer, &curves->geometry.point_data, - players, - curves->geometry.point_size, + point_layers, + curves->geometry.point_num, CD_MASK_ALL, &curves->id); CustomData_blend_write(writer, &curves->geometry.curve_data, - clayers, - curves->geometry.curve_size, + curve_layers, + curves->geometry.curve_num, CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); + BLO_write_int32_array(writer, curves->geometry.curve_num + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { BKE_animdata_blend_write(writer, curves->adt); } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); - } } static void curves_blend_read_data(BlendDataReader *reader, ID *id) @@ -169,11 +161,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) BKE_animdata_blend_read_data(reader, curves->adt); /* 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); + CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); + CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); @@ -247,7 +239,7 @@ void *BKE_curves_add(Main *bmain, const char *name) BoundBox *BKE_curves_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_CURVES); - Curves *curves = static_cast<Curves *>(ob->data); + const Curves *curves_id = static_cast<const Curves *>(ob->data); if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; @@ -256,11 +248,12 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) if (ob->runtime.bb == nullptr) { ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( + curves_id->geometry); float3 min(FLT_MAX); float3 max(-FLT_MAX); - if (!geometry.bounds_min_max(min, max)) { + if (!curves.bounds_min_max(min, max)) { min = float3(-1); max = float3(1); } @@ -364,19 +357,26 @@ namespace blender::bke { Curves *curves_new_nomain(const int points_num, const int curves_num) { - Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); - CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.resize(points_num, curves_num); - return curves; + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + curves.resize(points_num, curves_num); + return curves_id; } Curves *curves_new_nomain_single(const int points_num, const CurveType type) { - Curves *curves = curves_new_nomain(points_num, 1); - CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); - geometry.offsets_for_write().last() = points_num; - geometry.fill_curve_types(type); - return curves; + Curves *curves_id = curves_new_nomain(points_num, 1); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + curves.offsets_for_write().last() = points_num; + curves.fill_curve_types(type); + return curves_id; +} + +Curves *curves_new_nomain(CurvesGeometry curves) +{ + Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + bke::CurvesGeometry::wrap(curves_id->geometry) = std::move(curves); + return curves_id; } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 7a09b87490b..07e50eea88c 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -37,6 +37,8 @@ static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight"; static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode"; static const std::string ATTR_SURFACE_TRIANGLE_INDEX = "surface_triangle_index"; static const std::string ATTR_SURFACE_TRIANGLE_COORDINATE = "surface_triangle_coordinate"; +static const std::string ATTR_SELECTION_POINT_FLOAT = ".selection_point_float"; +static const std::string ATTR_SELECTION_CURVE_FLOAT = ".selection_curve_float"; /* -------------------------------------------------------------------- */ /** \name Constructors/Destructor @@ -46,10 +48,10 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) { } -CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) { - this->point_size = point_size; - this->curve_size = curve_size; + this->point_num = point_num; + this->curve_num = curve_num; CustomData_reset(&this->point_data); CustomData_reset(&this->curve_data); @@ -57,14 +59,16 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) CD_PROP_FLOAT3, CD_DEFAULT, nullptr, - this->point_size, + this->point_num, ATTR_POSITION.c_str()); - this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__); this->update_customdata_pointers(); this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); + /* Fill the type counts with the default so they're in a valid state. */ + this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; } /** @@ -72,15 +76,15 @@ CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) */ 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); + CustomData_free(&dst.point_data, dst.point_num); + CustomData_free(&dst.curve_data, dst.curve_num); + dst.point_num = src.point_num; + dst.curve_num = src.curve_num; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num); MEM_SAFE_FREE(dst.curve_offsets); - dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__); dst.offsets_for_write().copy_from(src.offsets()); dst.tag_topology_changed(); @@ -92,7 +96,7 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { copy_curves_geometry(*this, other); } @@ -108,15 +112,15 @@ CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) /* The source should be empty, but in a valid state so that using it further will work. */ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) { - dst.point_size = src.point_size; + dst.point_num = src.point_num; std::swap(dst.point_data, src.point_data); - CustomData_free(&src.point_data, src.point_size); - src.point_size = 0; + CustomData_free(&src.point_data, src.point_num); + src.point_num = 0; - dst.curve_size = src.curve_size; + dst.curve_num = src.curve_num; std::swap(dst.curve_data, src.curve_data); - CustomData_free(&src.curve_data, src.curve_size); - src.curve_size = 0; + CustomData_free(&src.curve_data, src.curve_num); + src.curve_num = 0; std::swap(dst.curve_offsets, src.curve_offsets); MEM_SAFE_FREE(src.curve_offsets); @@ -128,7 +132,7 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) - : CurvesGeometry(other.point_size, other.curve_size) + : CurvesGeometry(other.point_num, other.curve_num) { move_curves_geometry(*this, other); } @@ -143,8 +147,8 @@ CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other) CurvesGeometry::~CurvesGeometry() { - CustomData_free(&this->point_data, this->point_size); - CustomData_free(&this->curve_data, this->curve_size); + CustomData_free(&this->point_data, this->point_num); + CustomData_free(&this->curve_data, this->curve_num); MEM_SAFE_FREE(this->curve_offsets); MEM_delete(this->runtime); this->runtime = nullptr; @@ -156,7 +160,7 @@ CurvesGeometry::~CurvesGeometry() /** \name Accessors * \{ */ -static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) +static int domain_num(const CurvesGeometry &curves, const AttributeDomain domain) { return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num(); } @@ -178,15 +182,15 @@ static VArray<T> get_varray_attribute(const CurvesGeometry &curves, const StringRefNull name, const T default_value) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); const CustomData &custom_data = domain_custom_data(curves, domain); const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); if (data != nullptr) { - return VArray<T>::ForSpan(Span<T>(data, size)); + return VArray<T>::ForSpan(Span<T>(data, num)); } - return VArray<T>::ForSingle(default_value, size); + return VArray<T>::ForSingle(default_value, num); } template<typename T> @@ -194,7 +198,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, const AttributeDomain domain, const StringRefNull name) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomData &custom_data = domain_custom_data(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); @@ -202,7 +206,7 @@ static Span<T> get_span_attribute(const CurvesGeometry &curves, if (data == nullptr) { return {}; } - return {data, size}; + return {data, num}; } template<typename T> @@ -211,19 +215,19 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves, const StringRefNull name, const T default_value = T()) { - const int size = domain_size(curves, domain); + const int num = domain_num(curves, domain); const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>()); CustomData &custom_data = domain_custom_data(curves, domain); T *data = (T *)CustomData_duplicate_referenced_layer_named( - &custom_data, type, name.c_str(), size); + &custom_data, type, name.c_str(), num); if (data != nullptr) { - return {data, size}; + return {data, num}; } data = (T *)CustomData_add_layer_named( - &custom_data, type, CD_CALLOC, nullptr, size, name.c_str()); - MutableSpan<T> span = {data, size}; - if (size > 0 && span.first() != default_value) { + &custom_data, type, CD_CALLOC, nullptr, num, name.c_str()); + MutableSpan<T> span = {data, num}; + if (num > 0 && span.first() != default_value) { span.fill(default_value); } return span; @@ -250,6 +254,10 @@ void CurvesGeometry::fill_curve_types(const CurveType type) void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType type) { + if (selection.size() == this->curves_num()) { + this->fill_curve_types(type); + return; + } /* A potential performance optimization is only counting the changed indices. */ this->curve_types_for_write().fill_indices(selection, type); this->update_curve_types(); @@ -295,22 +303,22 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_size}; + return {(const float3 *)this->position, this->point_num}; } MutableSpan<float3> CurvesGeometry::positions_for_write() { 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}; + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); + return {(float3 *)this->position, this->point_num}; } Span<int> CurvesGeometry::offsets() const { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } MutableSpan<int> CurvesGeometry::offsets_for_write() { - return {this->curve_offsets, this->curve_size + 1}; + return {this->curve_offsets, this->curve_num + 1}; } VArray<bool> CurvesGeometry::cyclic() const @@ -432,18 +440,38 @@ MutableSpan<float2> CurvesGeometry::surface_triangle_coords_for_write() return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_COORDINATE); } +VArray<float> CurvesGeometry::selection_point_float() const +{ + return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f); +} + +MutableSpan<float> CurvesGeometry::selection_point_float_for_write() +{ + return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f); +} + +VArray<float> CurvesGeometry::selection_curve_float() const +{ + return get_varray_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f); +} + +MutableSpan<float> CurvesGeometry::selection_curve_float_for_write() +{ + return get_mutable_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Evaluation * \{ */ -template<typename SizeFn> void build_offsets(MutableSpan<int> offsets, const SizeFn &size_fn) +template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn) { int offset = 0; for (const int i : offsets.drop_back(1).index_range()) { offsets[i] = offset; - offset += size_fn(i); + offset += count_fn(i); } offsets.last() = offset; } @@ -466,7 +494,7 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const IndexRange points = curves.points_for_curve(curve_index); switch (types[curve_index]) { case CURVE_TYPE_CATMULL_ROM: - return curves::catmull_rom::calculate_evaluated_size( + return curves::catmull_rom::calculate_evaluated_num( points.size(), cyclic[curve_index], resolution[curve_index]); case CURVE_TYPE_POLY: return points.size(); @@ -478,11 +506,11 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, bezier_evaluated_offsets.slice(points)); return bezier_evaluated_offsets[points.last()]; case CURVE_TYPE_NURBS: - return curves::nurbs::calculate_evaluated_size(points.size(), - nurbs_orders[curve_index], - cyclic[curve_index], - resolution[curve_index], - KnotsMode(nurbs_knots_modes[curve_index])); + return curves::nurbs::calculate_evaluated_num(points.size(), + nurbs_orders[curve_index], + cyclic[curve_index], + resolution[curve_index], + KnotsMode(nurbs_knots_modes[curve_index])); } BLI_assert_unreachable(); return 0; @@ -527,19 +555,23 @@ Span<int> CurvesGeometry::evaluated_offsets() const IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, Vector<int64_t> &r_indices) const { + return this->indices_for_curve_type(type, this->curves_range(), r_indices); +} - VArray<int8_t> types = this->curve_types(); +IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type, + const IndexMask selection, + Vector<int64_t> &r_indices) const +{ + if (this->curve_type_counts()[type] == this->curves_num()) { + return selection; + } + const VArray<int8_t> types = this->curve_types(); if (types.is_single()) { - if (types.get_internal_single() == type) { - return IndexMask(types.size()); - } - return {}; + return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0); } Span<int8_t> types_span = types.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( - IndexMask(types.size()), 1024, r_indices, [&](const int index) { - return types_span[index] == type; - }); + selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; }); } void CurvesGeometry::ensure_nurbs_basis_cache() const @@ -577,8 +609,13 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const bool is_cyclic = cyclic[curve_index]; const KnotsMode mode = KnotsMode(knots_modes[curve_index]); - const int knots_size = curves::nurbs::knots_size(points.size(), order, is_cyclic); - Array<float> knots(knots_size); + if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) { + basis_caches[curve_index].invalid = true; + continue; + } + + const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic); + Array<float> knots(knots_num); curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots); curves::nurbs::calculate_basis_cache(points.size(), evaluated_points.size(), @@ -696,9 +733,6 @@ Span<float3> CurvesGeometry::evaluated_tangents() const threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points), cyclic[curve_index], tangents.slice(evaluated_points)); @@ -773,9 +807,6 @@ Span<float3> CurvesGeometry::evaluated_normals() const for (const int curve_index : curves_range) { const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } switch (normal_mode[curve_index]) { case NORMAL_MODE_Z_UP: curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), @@ -905,8 +936,8 @@ void CurvesGeometry::ensure_evaluated_lengths() const threading::isolate_task([&]() { /* Use an extra length value for the final cyclic segment for a consistent size * (see comment on #evaluated_length_cache). */ - const int total_size = this->evaluated_points_num() + this->curves_num(); - this->runtime->evaluated_length_cache.resize(total_size); + const int total_num = this->evaluated_points_num() + this->curves_num(); + this->runtime->evaluated_length_cache.resize(total_num); MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache; Span<float3> evaluated_positions = this->evaluated_positions(); @@ -916,9 +947,6 @@ void CurvesGeometry::ensure_evaluated_lengths() const for (const int curve_index : curves_range) { const bool cyclic = curves_cyclic[curve_index]; const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); - if (UNLIKELY(evaluated_points.is_empty())) { - continue; - } const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic); length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points), cyclic, @@ -938,13 +966,13 @@ void CurvesGeometry::ensure_evaluated_lengths() const void CurvesGeometry::resize(const int points_num, const int curves_num) { - if (points_num != this->point_size) { + if (points_num != this->point_num) { CustomData_realloc(&this->point_data, points_num); - this->point_size = points_num; + this->point_num = points_num; } - if (curves_num != this->curve_size) { + if (curves_num != this->curve_num) { CustomData_realloc(&this->curve_data, curves_num); - this->curve_size = curves_num; + this->curve_num = curves_num; this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); @@ -1196,6 +1224,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } }); + new_curves.update_curve_types(); + return new_curves; } diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc new file mode 100644 index 00000000000..78c2382b62f --- /dev/null +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves_utils.hh" + +namespace blender::bke::curves { + +void fill_curve_counts(const bke::CurvesGeometry &curves, + const Span<IndexRange> curve_ranges, + MutableSpan<int> counts) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { + for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { + threading::parallel_for(curves_range, 4096, [&](IndexRange range) { + for (const int i : range) { + counts[i] = curves.points_for_curve(i).size(); + } + }); + } + }); +} + +void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets, const int start_offset) +{ + int offset = start_offset; + for (const int i : counts_to_offsets.index_range().drop_back(1)) { + const int count = counts_to_offsets[i]; + BLI_assert(count > 0); + counts_to_offsets[i] = offset; + offset += count; + } + counts_to_offsets.last() = offset; +} + +} // namespace blender::bke::curves diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 1d9f377abec..058230745e7 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -25,6 +25,7 @@ #include "BLI_math_vector.hh" #include "BLI_mempool.h" #include "BLI_path_util.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" @@ -55,6 +56,8 @@ #include "data_transfer_intern.h" using blender::IndexRange; +using blender::Span; +using blender::Vector; /* number of layers to add when growing a CustomData object */ #define CUSTOMDATA_GROW 5 @@ -2593,36 +2596,48 @@ void CustomData_set_layer_stencil(CustomData *data, int type, int n) void CustomData_set_layer_active_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active = n - i; + data->layers[i].active = n - layer_index; } } } void CustomData_set_layer_render_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active_rnd = n - i; + data->layers[i].active_rnd = n - layer_index; } } } void CustomData_set_layer_clone_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active_clone = n - i; + data->layers[i].active_clone = n - layer_index; } } } void CustomData_set_layer_stencil_index(CustomData *data, int type, int n) { + const int layer_index = data->typemap[type]; + BLI_assert(customdata_typemap_is_valid(data)); + for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { - data->layers[i].active_mask = n - i; + data->layers[i].active_mask = n - layer_index; } } } @@ -3664,7 +3679,7 @@ void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) chunksize = bm_mesh_chunksize_default.totface; break; default: - BLI_assert(0); + BLI_assert_unreachable(); chunksize = 512; break; } @@ -4355,45 +4370,19 @@ void CustomData_file_write_info(int type, const char **r_struct_name, int *r_str *r_struct_num = typeInfo->structnum; } -void CustomData_blend_write_prepare(CustomData *data, - CustomDataLayer **r_write_layers, - CustomDataLayer *write_layers_buff, - size_t write_layers_size) +void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16> &layers_to_write) { - CustomDataLayer *write_layers = write_layers_buff; - const size_t chunk_size = (write_layers_size > 0) ? write_layers_size : CD_TEMP_CHUNK_SIZE; - - const int totlayer = data->totlayer; - int i, j; - - for (i = 0, j = 0; i < totlayer; i++) { - CustomDataLayer *layer = &data->layers[i]; - /* Layers with this flag set are not written to file. */ - if ((layer->flag & CD_FLAG_NOCOPY) || layer->anonymous_id != nullptr) { - data->totlayer--; - // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); + for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { + if (layer.flag & CD_FLAG_NOCOPY) { + continue; } - else { - if (UNLIKELY((size_t)j >= write_layers_size)) { - if (write_layers == write_layers_buff) { - write_layers = (CustomDataLayer *)MEM_malloc_arrayN( - (write_layers_size + chunk_size), sizeof(*write_layers), __func__); - if (write_layers_buff) { - memcpy(write_layers, write_layers_buff, sizeof(*write_layers) * write_layers_size); - } - } - else { - write_layers = (CustomDataLayer *)MEM_reallocN( - write_layers, sizeof(*write_layers) * (write_layers_size + chunk_size)); - } - write_layers_size += chunk_size; - } - write_layers[j++] = *layer; + if (layer.anonymous_id != nullptr) { + continue; } + layers_to_write.append(layer); } - BLI_assert(j == data->totlayer); - data->maxlayer = data->totlayer; /* We only write that much of data! */ - *r_write_layers = write_layers; + data.totlayer = layers_to_write.size(); + data.maxlayer = data.totlayer; } int CustomData_sizeof(int type) @@ -5155,12 +5144,12 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, /** \name Custom Data IO * \{ */ -static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) +static void write_mdisps(BlendWriter *writer, int count, const MDisps *mdlist, int external) { if (mdlist) { BLO_write_struct_array(writer, MDisps, count, mdlist); for (int i = 0; i < count; i++) { - MDisps *md = &mdlist[i]; + const MDisps *md = &mdlist[i]; if (md->disps) { if (!external) { BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); @@ -5174,12 +5163,14 @@ static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int ext } } -static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) +static void write_grid_paint_mask(BlendWriter *writer, + int count, + const GridPaintMask *grid_paint_mask) { if (grid_paint_mask) { BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); for (int i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; + const GridPaintMask *gpm = &grid_paint_mask[i]; if (gpm->data) { const int gridsize = BKE_ccg_gridsize(gpm->level); BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); @@ -5190,7 +5181,7 @@ static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask void CustomData_blend_write(BlendWriter *writer, CustomData *data, - CustomDataLayer *layers, + Span<CustomDataLayer> layers_to_write, int count, CustomDataMask cddata_mask, ID *id) @@ -5200,55 +5191,50 @@ void CustomData_blend_write(BlendWriter *writer, CustomData_external_write(data, id, cddata_mask, count, 0); } - BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers); - - for (int i = 0; i < data->totlayer; i++) { - CustomDataLayer *layer = &layers[i]; + BLO_write_struct_array_at_address( + writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data()); - if (layer->type == CD_MDEFORMVERT) { - /* layer types that allocate own memory need special handling */ - BKE_defvert_blend_write(writer, count, static_cast<struct MDeformVert *>(layer->data)); - } - else if (layer->type == CD_MDISPS) { - write_mdisps( - writer, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL); - } - else if (layer->type == CD_PAINT_MASK) { - const float *layer_data = static_cast<const float *>(layer->data); - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_SCULPT_FACE_SETS) { - const float *layer_data = static_cast<const float *>(layer->data); - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_GRID_PAINT_MASK) { - write_grid_paint_mask(writer, count, static_cast<GridPaintMask *>(layer->data)); - } - else if (layer->type == CD_FACEMAP) { - const int *layer_data = static_cast<const int *>(layer->data); - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_PROP_BOOL) { - const bool *layer_data = static_cast<const bool *>(layer->data); - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_CREASE) { - const float *layer_data = static_cast<const float *>(layer->data); - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else { - const char *structname; - int structnum; - CustomData_file_write_info(layer->type, &structname, &structnum); - if (structnum) { - int datasize = structnum * count; - BLO_write_struct_array_by_name(writer, structname, datasize, layer->data); - } - else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ - printf("%s error: layer '%s':%d - can't be written to file\n", - __func__, - structname, - layer->type); + for (const CustomDataLayer &layer : layers_to_write) { + switch (layer.type) { + case CD_MDEFORMVERT: + BKE_defvert_blend_write(writer, count, static_cast<const MDeformVert *>(layer.data)); + break; + case CD_MDISPS: + write_mdisps( + writer, count, static_cast<const MDisps *>(layer.data), layer.flag & CD_FLAG_EXTERNAL); + break; + case CD_PAINT_MASK: + BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data)); + break; + case CD_SCULPT_FACE_SETS: + BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data)); + break; + case CD_GRID_PAINT_MASK: + write_grid_paint_mask(writer, count, static_cast<const GridPaintMask *>(layer.data)); + break; + case CD_FACEMAP: + BLO_write_raw(writer, sizeof(int) * count, static_cast<const int *>(layer.data)); + break; + case CD_PROP_BOOL: + BLO_write_raw(writer, sizeof(bool) * count, static_cast<const bool *>(layer.data)); + break; + case CD_CREASE: + BLO_write_raw(writer, sizeof(float) * count, static_cast<const float *>(layer.data)); + break; + default: { + const char *structname; + int structnum; + CustomData_file_write_info(layer.type, &structname, &structnum); + if (structnum) { + int datasize = structnum * count; + BLO_write_struct_array_by_name(writer, structname, datasize, layer.data); + } + else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ + printf("%s error: layer '%s':%d - can't be written to file\n", + __func__, + structname, + layer.type); + } } } } diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 9716b4909b2..cbfaf2831d1 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -268,11 +268,11 @@ static bool cdf_write_header(CDataFile *cdf) return true; } -bool cdf_read_open(CDataFile *cdf, const char *filename) +bool cdf_read_open(CDataFile *cdf, const char *filepath) { FILE *f; - f = BLI_fopen(filename, "rb"); + f = BLI_fopen(filepath, "rb"); if (!f) { return false; } @@ -333,14 +333,14 @@ void cdf_read_close(CDataFile *cdf) } } -bool cdf_write_open(CDataFile *cdf, const char *filename) +bool cdf_write_open(CDataFile *cdf, const char *filepath) { CDataFileHeader *header; CDataFileImageHeader *image; CDataFileMeshHeader *mesh; FILE *f; - f = BLI_fopen(filename, "wb"); + f = BLI_fopen(filepath, "wb"); if (!f) { return false; } @@ -402,9 +402,9 @@ void cdf_write_close(CDataFile *cdf) } } -void cdf_remove(const char *filename) +void cdf_remove(const char *filepath) { - BLI_delete(filename, false, false); + BLI_delete(filepath, false, false); } /********************************** Layers ***********************************/ diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6a3f6a47f5e..196a6a00ade 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -222,7 +222,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) case DT_TYPE_MPROPCOL_LOOP: return CD_PROP_COLOR; default: - BLI_assert(0); + BLI_assert_unreachable(); } return 0; /* Should never be reached! */ } @@ -477,7 +477,7 @@ static void data_transfer_layersmapping_add_item_cd(ListBase *r_map, const int mix_mode, const float mix_factor, const float *mix_weights, - void *data_src, + const void *data_src, void *data_dst, cd_datatransfer_interp interp, void *interp_data) @@ -532,7 +532,8 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map cd_datatransfer_interp interp, void *interp_data) { - void *data_src, *data_dst = NULL; + const void *data_src; + void *data_dst = NULL; int idx_src = num_layers_src; int idx_dst, tot_dst = CustomData_number_of_layers(cd_dst, cddata_type); bool *data_dst_to_delete = NULL; @@ -695,7 +696,8 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map, void *interp_data) { int idx_src, idx_dst; - void *data_src, *data_dst = NULL; + const void *data_src; + void *data_dst = NULL; if (CustomData_layertype_is_singleton(cddata_type)) { if (!(data_src = CustomData_get_layer(cd_src, cddata_type))) { @@ -1369,7 +1371,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, /* Assumed always true if not using an evaluated mesh as destination. */ bool dirty_nors_dst = true; - MDeformVert *mdef = NULL; + const MDeformVert *mdef = NULL; int vg_idx = -1; float *weights[DATAMAX] = {NULL}; @@ -1509,7 +1511,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[VDATA]) { weights[VDATA] = MEM_mallocN(sizeof(*(weights[VDATA])) * (size_t)num_verts_dst, __func__); BKE_defvert_extract_vgroup_to_vertweights( - mdef, vg_idx, num_verts_dst, weights[VDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, invert_vgroup, weights[VDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1588,7 +1590,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[EDATA]) { weights[EDATA] = MEM_mallocN(sizeof(*weights[EDATA]) * (size_t)num_edges_dst, __func__); BKE_defvert_extract_vgroup_to_edgeweights( - mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, weights[EDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, edges_dst, num_edges_dst, invert_vgroup, weights[EDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1683,7 +1685,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (mdef && vg_idx != -1 && !weights[LDATA]) { weights[LDATA] = MEM_mallocN(sizeof(*weights[LDATA]) * (size_t)num_loops_dst, __func__); BKE_defvert_extract_vgroup_to_loopweights( - mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, weights[LDATA], invert_vgroup); + mdef, vg_idx, num_verts_dst, loops_dst, num_loops_dst, invert_vgroup, weights[LDATA]); } if (data_transfer_layersmapping_generate(&lay_map, @@ -1769,8 +1771,8 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, num_loops_dst, polys_dst, num_polys_dst, - weights[PDATA], - invert_vgroup); + invert_vgroup, + weights[PDATA]); } if (data_transfer_layersmapping_generate(&lay_map, diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 1d276e9108c..ebe06fa85eb 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -147,9 +147,9 @@ void BKE_defvert_copy_index(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const int defgroup_src) { - MDeformWeight *dw_src, *dw_dst; + MDeformWeight *dw_dst; - dw_src = BKE_defvert_find_index(dvert_src, defgroup_src); + const MDeformWeight *dw_src = BKE_defvert_find_index(dvert_src, defgroup_src); if (dw_src) { /* Source is valid, ensure destination is created. */ @@ -1008,11 +1008,11 @@ void BKE_defvert_array_free(MDeformVert *dvert, int totvert) MEM_freeN(dvert); } -void BKE_defvert_extract_vgroup_to_vertweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert, const int defgroup, const int num_verts, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_verts; @@ -1027,20 +1027,20 @@ void BKE_defvert_extract_vgroup_to_vertweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_edgeweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MEdge *edges, const int num_edges, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_edges; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MEdge *me = &edges[i]; @@ -1055,20 +1055,20 @@ void BKE_defvert_extract_vgroup_to_edgeweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_loopweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int num_loops, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_loops; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MLoop *ml = &loops[i]; @@ -1083,22 +1083,22 @@ void BKE_defvert_extract_vgroup_to_loopweights(MDeformVert *dvert, } } -void BKE_defvert_extract_vgroup_to_polyweights(MDeformVert *dvert, +void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int UNUSED(num_loops), MPoly *polys, const int num_polys, - float *r_weights, - const bool invert_vgroup) + const bool invert_vgroup, + float *r_weights) { if (dvert && defgroup != -1) { int i = num_polys; float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__); BKE_defvert_extract_vgroup_to_vertweights( - dvert, defgroup, num_verts, tmp_weights, invert_vgroup); + dvert, defgroup, num_verts, invert_vgroup, tmp_weights); while (i--) { MPoly *mp = &polys[i]; @@ -1193,7 +1193,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, const bool use_delete, Object *ob_src, Object *ob_dst, - MDeformVert *data_src, + const MDeformVert *data_src, MDeformVert *data_dst, CustomData *UNUSED(cd_src), CustomData *cd_dst, @@ -1348,7 +1348,6 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, const int tolayers) { int idx_src, idx_dst; - MDeformVert *data_src, *data_dst = NULL; const size_t elem_size = sizeof(*((MDeformVert *)NULL)); @@ -1370,9 +1369,9 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, return true; } - data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT); + const MDeformVert *data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT); - data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT); + MDeformVert *data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT); if (data_dst && use_dupref_dst && r_map) { /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */ data_dst = CustomData_duplicate_referenced_layer(cd_dst, CD_MDEFORMVERT, num_elem_dst); @@ -1562,7 +1561,7 @@ void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase) } } -void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist) +void BKE_defvert_blend_write(BlendWriter *writer, int count, const MDeformVert *dvlist) { if (dvlist == NULL) { return; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 22341f98375..48f2d66c1cd 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -1607,7 +1607,6 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface const MLoop *mloop = mesh->mloop; const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); const int tottri = BKE_mesh_runtime_looptri_len(mesh); - const MLoopUV *mloopuv = NULL; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; @@ -1617,7 +1616,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface /* get uv map */ CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, surface->init_layername, uvname); - mloopuv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); + const MLoopUV *mloopuv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); if (!mloopuv) { return; } @@ -1675,7 +1674,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface } else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh); - MLoopCol *col = CustomData_get_layer_named( + const MLoopCol *col = CustomData_get_layer_named( &mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername); if (!col) { return; diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index d176bf41254..a952da6fa52 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -15,6 +15,7 @@ #include "BLI_math.h" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" #include "BKE_lib_id.h" diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 34357c3e454..952d5df299c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -351,8 +351,8 @@ FCurve *BKE_fcurve_find_by_rna(PointerRNA *ptr, NULL, ptr, prop, rnaindex, r_adt, r_action, r_driven, r_special); } -FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C, - PointerRNA *ptr, +FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *UNUSED(C), + const PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_animdata, @@ -361,7 +361,6 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C, bool *r_special) { FCurve *fcu = NULL; - PointerRNA tptr = *ptr; *r_driven = false; *r_special = false; @@ -388,79 +387,51 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C, } /* There must be some RNA-pointer + property combo. */ - if (prop && tptr.owner_id && RNA_property_animateable(&tptr, prop)) { - AnimData *adt = BKE_animdata_from_id(tptr.owner_id); - int step = ( - /* Always 1 in case we have no context (can't check in 'ancestors' of given RNA ptr). */ - C ? 2 : 1); - char *path = NULL; - - if (!adt && C) { - path = RNA_path_from_ID_to_property(&tptr, prop); - adt = BKE_animdata_from_id(tptr.owner_id); - step--; - } - - /* Standard F-Curve - Animation (Action) or Drivers. */ - while (adt && step--) { - if ((adt->action == NULL || adt->action->curves.first == NULL) && - (adt->drivers.first == NULL)) { - continue; - } - - /* XXX This function call can become a performance bottleneck. */ - if (step) { - path = RNA_path_from_ID_to_property(&tptr, prop); - } - if (path == NULL) { - continue; - } + if (!prop || !ptr->owner_id || !RNA_property_animateable(ptr, prop)) { + return fcu; + } - /* XXX: The logic here is duplicated with a function up above. */ - /* animation takes priority over drivers. */ - if (adt->action && adt->action->curves.first) { - fcu = BKE_fcurve_find(&adt->action->curves, path, rnaindex); + AnimData *adt = BKE_animdata_from_id(ptr->owner_id); + if (adt == NULL) { + return fcu; + } - if (fcu && r_action) { - *r_action = adt->action; - } - } + const bool has_action_fcurves = adt->action != NULL && + !BLI_listbase_is_empty(&adt->action->curves); + const bool has_drivers = !BLI_listbase_is_empty(&adt->drivers); - /* If not animated, check if driven. */ - if (!fcu && (adt->drivers.first)) { - fcu = BKE_fcurve_find(&adt->drivers, path, rnaindex); + /* XXX This function call can become a performance bottleneck. */ + char *path = RNA_path_from_ID_to_property(ptr, prop); - if (fcu) { - if (r_animdata) { - *r_animdata = adt; - } - *r_driven = true; - } - } + /* Standard F-Curve - Animation (Action) or Drivers. */ + /* Animation takes priority over drivers. */ + /* XXX: The logic here is duplicated with a function up above. */ + if (has_action_fcurves) { + fcu = BKE_fcurve_find(&adt->action->curves, path, rnaindex); - if (fcu && r_action) { - if (r_animdata) { - *r_animdata = adt; - } + if (fcu) { + if (r_action) { *r_action = adt->action; - break; } + if (r_animdata) { + *r_animdata = adt; + } + } + } - if (step) { - char *tpath = path ? path : RNA_path_from_ID_to_property(&tptr, prop); - if (tpath && tpath != path) { - MEM_freeN(path); - path = tpath; - adt = BKE_animdata_from_id(tptr.owner_id); - } - else { - adt = NULL; - } + /* If not animated, check if driven. */ + if (fcu == NULL && has_drivers) { + fcu = BKE_fcurve_find(&adt->drivers, path, rnaindex); + + if (fcu) { + if (r_animdata) { + *r_animdata = adt; } + *r_driven = true; } - MEM_SAFE_FREE(path); } + MEM_SAFE_FREE(path); return fcu; } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 5de13fbdbed..06d32d5bfd4 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1824,7 +1824,7 @@ static void sample_mesh(FluidFlowSettings *ffs, const float *vert_vel, bool has_velocity, int defgrp_index, - MDeformVert *dvert, + const MDeformVert *dvert, float x, float y, float z) @@ -2008,7 +2008,7 @@ typedef struct EmitFromDMData { const MLoop *mloop; const MLoopTri *mlooptri; const MLoopUV *mloopuv; - MDeformVert *dvert; + const MDeformVert *dvert; int defgrp_index; BVHTreeFromMesh *tree; @@ -2074,14 +2074,8 @@ static void emit_from_mesh( Object *flow_ob, FluidDomainSettings *fds, FluidFlowSettings *ffs, FluidObjectBB *bb, float dt) { if (ffs->mesh) { - Mesh *me = NULL; - MVert *mvert = NULL; - const MLoopTri *mlooptri = NULL; - const MLoop *mloop = NULL; - const MLoopUV *mloopuv = NULL; - MDeformVert *dvert = NULL; BVHTreeFromMesh tree_data = {NULL}; - int numverts, i; + int i; float *vert_vel = NULL; bool has_velocity = false; @@ -2092,7 +2086,7 @@ static void emit_from_mesh( /* Copy mesh for thread safety as we modify it. * Main issue is its VertArray being modified, then replaced and freed. */ - me = BKE_mesh_copy_for_eval(ffs->mesh, true); + Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, true); /* Duplicate vertices to modify. */ if (me->mvert) { @@ -2100,12 +2094,12 @@ static void emit_from_mesh( CustomData_set_layer(&me->vdata, CD_MVERT, me->mvert); } - mvert = me->mvert; - mloop = me->mloop; - mlooptri = BKE_mesh_runtime_looptri_ensure(me); - numverts = me->totvert; - dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT); - mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name); + MVert *mvert = me->mvert; + const MLoop *mloop = me->mloop; + const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); + const int numverts = me->totvert; + const MDeformVert *dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT); + const MLoopUV *mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name); if (ffs->flags & FLUID_FLOW_INITVELOCITY) { vert_vel = MEM_callocN(sizeof(float[3]) * numverts, "manta_flow_velocity"); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f409389e463..a28afc8ddca 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -114,7 +114,7 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_num(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -251,8 +251,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize(const IndexMask mask, MutableSpan<T> r_span) const final { - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -273,8 +273,8 @@ template<typename T> class VArray_For_SplineToPoint final : public VArrayImpl<T> void materialize_to_uninitialized(const IndexMask mask, MutableSpan<T> r_span) const final { T *dst = r_span.data(); - const int total_size = offsets_.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : original_data_.index_range()) { const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; @@ -415,7 +415,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; } }; @@ -495,8 +495,8 @@ static void point_attribute_materialize(Span<Span<T>> data, const IndexMask mask, MutableSpan<T> r_span) { - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -541,8 +541,8 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, MutableSpan<T> r_span) { T *dst = r_span.data(); - const int total_size = offsets.last(); - if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + const int total_num = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_num)) { for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; @@ -589,13 +589,13 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, case AttributeInit::Type::VArray: return static_cast<const AttributeInitVArray &>(initializer).varray; case AttributeInit::Type::MoveArray: - int total_size = 0; + int total_num = 0; for (const SplinePtr &spline : splines) { - total_size += spline->size(); + total_num += spline->size(); } return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type), static_cast<const AttributeInitMove &>(initializer).data, - total_size)); + total_num)); } BLI_assert_unreachable(); return {}; @@ -1168,7 +1168,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { } return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index bc9bba3ee2f..9b023d12a7d 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -135,12 +135,12 @@ const Curve *CurveComponent::get_curve_for_render() const /** \} */ +namespace blender::bke { + /* -------------------------------------------------------------------- */ /** \name Curve Normals Access * \{ */ -namespace blender::bke { - static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves) { const VArray<int8_t> types = curves.curve_types(); @@ -237,26 +237,89 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri return nullptr; } -} // namespace blender::bke +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Length Field Input + * \{ */ + +static VArray<float> construct_curve_length_gvarray(const CurveComponent &component, + const AttributeDomain domain) +{ + if (!component.has_curves()) { + return {}; + } + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + + curves.ensure_evaluated_lengths(); + + VArray<bool> cyclic = curves.cyclic(); + VArray<float> lengths = VArray<float>::ForFunc( + curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { + return curves.evaluated_length_total_for_curve(index, cyclic[index]); + }); + + if (domain == ATTR_DOMAIN_CURVE) { + return lengths; + } + + if (domain == ATTR_DOMAIN_POINT) { + return component.attribute_try_adapt_domain<float>( + std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + } + + return {}; +} + +CurveLengthFieldInput::CurveLengthFieldInput() + : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") +{ + category_ = Category::Generated; +} + +GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const +{ + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_curve_length_gvarray(curve_component, domain); + } + return {}; +} + +uint64_t CurveLengthFieldInput::hash() const +{ + /* Some random constant hash. */ + return 3549623580; +} + +bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; +} /** \} */ +} // namespace blender::bke + /* -------------------------------------------------------------------- */ /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponent::attribute_domain_num(const AttributeDomain domain) const { if (curves_ == nullptr) { return 0; } - const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_->geometry); if (domain == ATTR_DOMAIN_POINT) { - return geometry.points_num(); + return curves.points_num(); } if (domain == ATTR_DOMAIN_CURVE) { - return geometry.curves_num(); + return curves.curves_num(); } return 0; } diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0dc6f486d28..e56a7ca4dd8 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -79,7 +79,7 @@ void InstancesComponent::add_instance(const int instance_handle, const float4x4 BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - attributes_.reallocate(this->instances_amount()); + attributes_.reallocate(this->instances_num()); } blender::Span<int> InstancesComponent::instance_reference_handles() const @@ -183,7 +183,7 @@ void InstancesComponent::remove_unused_references() using namespace blender; using namespace blender::bke; - const int tot_instances = this->instances_amount(); + const int tot_instances = this->instances_num(); const int tot_references_before = references_.size(); if (tot_instances == 0) { @@ -258,12 +258,12 @@ void InstancesComponent::remove_unused_references() }); } -int InstancesComponent::instances_amount() const +int InstancesComponent::instances_num() const { return instance_transforms_.size(); } -int InstancesComponent::references_amount() const +int InstancesComponent::references_num() const { return references_.size(); } @@ -358,7 +358,7 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const } } else { - almost_unique_ids_.reinitialize(this->instances_amount()); + almost_unique_ids_.reinitialize(this->instances_num()); for (const int i : almost_unique_ids_.index_range()) { almost_unique_ids_[i] = i; } @@ -366,12 +366,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +int InstancesComponent::attribute_domain_num(const AttributeDomain domain) const { if (domain != ATTR_DOMAIN_INSTANCE) { return 0; } - return this->instances_amount(); + return this->instances_num(); } blender::bke::CustomDataAttributes &InstancesComponent::attributes() diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index fb39861d3e7..5ac9a03f43c 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -169,7 +169,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_size(const AttributeDomain domain) const +int MeshComponent::attribute_domain_num(const AttributeDomain domain) const { if (mesh_ == nullptr) { return 0; @@ -839,20 +839,20 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -static GVArray make_derived_read_attribute(const void *data, const int domain_size) +static GVArray make_derived_read_attribute(const void *data, const int domain_num) { return VArray<ElemT>::template ForDerivedSpan<StructT, GetFunc>( - Span<StructT>((const StructT *)data, domain_size)); + Span<StructT>((const StructT *)data, domain_num)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), void (*SetFunc)(StructT &, ElemT)> -static GVMutableArray make_derived_write_attribute(void *data, const int domain_size) +static GVMutableArray make_derived_write_attribute(void *data, const int domain_num) { return VMutableArray<ElemT>::template ForDerivedSpan<StructT, GetFunc, SetFunc>( - MutableSpan<StructT>((StructT *)data, domain_size)); + MutableSpan<StructT>((StructT *)data, domain_num)); } static float3 get_vertex_position(const MVert &vert) @@ -1160,7 +1160,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0; + return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; } }; diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 400e0ea5e15..6de123c7cb9 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -104,7 +104,7 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const +int PointCloudComponent::attribute_domain_num(const AttributeDomain domain) const { if (pointcloud_ == nullptr) { return 0; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 2bd8b643899..40e36ced199 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -272,7 +272,7 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read<InstancesComponent>(); - return component != nullptr && component->instances_amount() >= 1; + return component != nullptr && component->instances_num() >= 1; } bool GeometrySet::has_volume() const diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 699f8b356bd..f86e947910b 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2803,7 +2803,7 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob) /* Iterate over frame range. */ for (bGPDframe *gpf = gpf_start; gpf != NULL && gpf != gpf_end; gpf = gpf->next) { - /* Skip frames without a valid onion skinning id (note: active frame has one). */ + /* Skip frames without a valid onion skinning id (NOTE: active frame has one). */ if (gpf->runtime.onion_id == INT_MAX) { continue; } diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 2ba6510ee71..f59f5352aad 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -114,32 +114,35 @@ static void icon_free(void *val) static void icon_free_data(int icon_id, Icon *icon) { - if (icon->obj_type == ICON_DATA_ID) { - ((ID *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_IMBUF) { - ImBuf *imbuf = (ImBuf *)icon->obj; - if (imbuf) { - IMB_freeImBuf(imbuf); + switch (icon->obj_type) { + case ICON_DATA_ID: + ((ID *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_IMBUF: { + ImBuf *imbuf = (ImBuf *)icon->obj; + if (imbuf) { + IMB_freeImBuf(imbuf); + } + break; } - } - else if (icon->obj_type == ICON_DATA_PREVIEW) { - ((PreviewImage *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_GPLAYER) { - ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_GEOM) { - ((struct Icon_Geom *)(icon->obj))->icon_id = 0; - } - else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { - StudioLight *sl = (StudioLight *)icon->obj; - if (sl != nullptr) { - BKE_studiolight_unset_icon_id(sl, icon_id); + case ICON_DATA_PREVIEW: + ((PreviewImage *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_GPLAYER: + ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; + break; + case ICON_DATA_GEOM: + ((struct Icon_Geom *)(icon->obj))->icon_id = 0; + break; + case ICON_DATA_STUDIOLIGHT: { + StudioLight *sl = (StudioLight *)icon->obj; + if (sl != nullptr) { + BKE_studiolight_unset_icon_id(sl, icon_id); + } + break; } - } - else { - BLI_assert(0); + default: + BLI_assert_unreachable(); } } @@ -241,16 +244,16 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size) return prv_img; } -static PreviewImage *previewimg_deferred_create(const char *path, int source) +static PreviewImage *previewimg_deferred_create(const char *filepath, int source) { - /* We pack needed data for lazy loading (source type, in a single char, and path). */ - const size_t deferred_data_size = strlen(path) + 2; + /* We pack needed data for lazy loading (source type, in a single char, and filepath). */ + const size_t deferred_data_size = strlen(filepath) + 2; char *deferred_data; PreviewImage *prv = previewimg_create_ex(deferred_data_size); deferred_data = (char *)PRV_DEFERRED_DATA(prv); deferred_data[0] = source; - memcpy(&deferred_data[1], path, deferred_data_size - 1); + memcpy(&deferred_data[1], filepath, deferred_data_size - 1); return prv; } @@ -393,7 +396,7 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id) return nullptr; } -void BKE_previewimg_id_custom_set(ID *id, const char *path) +void BKE_previewimg_id_custom_set(ID *id, const char *filepath) { PreviewImage **prv = BKE_previewimg_id_get_p(id); @@ -403,7 +406,7 @@ void BKE_previewimg_id_custom_set(ID *id, const char *path) if (*prv) { BKE_previewimg_deferred_release(*prv); } - *prv = previewimg_deferred_create(path, THB_SOURCE_IMAGE); + *prv = previewimg_deferred_create(filepath, THB_SOURCE_IMAGE); /* Can't lazy-render the preview on access. ID previews are saved to files and we want them to be * there in time. Not only if something happened to have accessed it meanwhile. */ @@ -458,7 +461,7 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) } PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, - const char *path, + const char *filepath, const int source, bool force_update) { @@ -476,8 +479,8 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, if (prv && force_update) { const char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); - if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) { - /* If same path, no need to re-allocate preview, just clear it up. */ + if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], filepath)) { + /* If same filepath, no need to re-allocate preview, just clear it up. */ BKE_previewimg_clear(prv); } else { @@ -486,7 +489,7 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, } if (!prv) { - prv = previewimg_deferred_create(path, source); + prv = previewimg_deferred_create(filepath, source); force_update = true; } @@ -521,10 +524,10 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) ImBuf *thumb; char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); int source = prv_deferred_data[0]; - char *path = &prv_deferred_data[1]; + char *filepath = &prv_deferred_data[1]; int icon_w, icon_h; - thumb = IMB_thumb_manage(path, THB_LARGE, (ThumbSource)source); + thumb = IMB_thumb_manage(filepath, THB_LARGE, (ThumbSource)source); if (thumb) { /* PreviewImage assumes premultiplied alhpa... */ diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 7b8dfdc690c..e99230ef523 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -872,7 +872,7 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is case IDP_ID: return (IDP_Id(prop1) == IDP_Id(prop2)); default: - BLI_assert(0); + BLI_assert_unreachable(); break; } diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index 2ffa5125b1d..5b7484ab422 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -181,7 +181,7 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro break; } default: { - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 4bf25c24235..06a5d877882 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -182,9 +182,7 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - image_dst->gputexture[i][eye][resolution] = nullptr; - } + image_dst->gputexture[i][eye] = nullptr; } } @@ -236,24 +234,21 @@ static void image_foreach_cache(ID *id, key.offset_in_ID = offsetof(Image, cache); function_callback(id, &key, (void **)&image->cache, 0, user_data); - auto gputexture_offset = [image](int target, int eye, int resolution) { + auto gputexture_offset = [image](int target, int eye) { constexpr size_t base_offset = offsetof(Image, gputexture); - struct GPUTexture **first = &image->gputexture[0][0][0]; - const size_t array_offset = sizeof(*first) * - (&image->gputexture[target][eye][resolution] - first); + struct GPUTexture **first = &image->gputexture[0][0]; + const size_t array_offset = sizeof(*first) * (&image->gputexture[target][eye] - first); return base_offset + array_offset; }; for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *texture = image->gputexture[a][eye][resolution]; - if (texture == nullptr) { - continue; - } - key.offset_in_ID = gputexture_offset(a, eye, resolution); - function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); + GPUTexture *texture = image->gputexture[a][eye]; + if (texture == nullptr) { + continue; } + key.offset_in_ID = gputexture_offset(a, eye); + function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); } } @@ -335,9 +330,7 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ima->runtime.partial_update_user = nullptr; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - ima->gputexture[i][j][resolution] = nullptr; - } + ima->gputexture[i][j] = nullptr; } } @@ -721,6 +714,9 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) imapf_src = imapf_src->next) { ImagePackedFile *imapf_dst = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)")); + + imapf_dst->view = imapf_src->view; + imapf_dst->tile_number = imapf_src->tile_number; STRNCPY(imapf_dst->filepath, imapf_src->filepath); if (imapf_src->packedfile) { @@ -781,10 +777,8 @@ bool BKE_image_has_opengl_texture(Image *ima) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - return true; - } + if (ima->gputexture[i][eye] != nullptr) { + return true; } } } @@ -1197,7 +1191,8 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) } /** Pack image buffer to memory as PNG or EXR. */ -static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) +static bool image_memorypack_imbuf( + Image *ima, ImBuf *ibuf, int view, int tile_number, const char *filepath) { ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; @@ -1219,6 +1214,8 @@ static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath imapf = static_cast<ImagePackedFile *>(MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile")); STRNCPY(imapf->filepath, filepath); imapf->packedfile = pf; + imapf->view = view; + imapf->tile_number = tile_number; BLI_addtail(&ima->packedfiles, imapf); ibuf->encodedbuffer = nullptr; @@ -1234,42 +1231,47 @@ bool BKE_image_memorypack(Image *ima) image_free_packedfiles(ima); - if (BKE_image_is_multiview(ima)) { - /* Store each view as a separate packed files with R_IMF_VIEWS_INDIVIDUAL. */ - ImageView *iv; - int i; + const int tot_viewfiles = image_num_viewfiles(ima); + const bool is_tiled = (ima->source == IMA_SRC_TILED); + const bool is_multiview = BKE_image_is_multiview(ima); - for (i = 0, iv = static_cast<ImageView *>(ima->views.first); iv; - iv = static_cast<ImageView *>(iv->next), i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, nullptr); + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + char tiled_filepath[FILE_MAX]; + for (int view = 0; view < tot_viewfiles; view++) { + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + int index = (is_multiview || is_tiled) ? view : IMA_NO_INDEX; + int entry = is_tiled ? tile->tile_number : 0; + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, nullptr); if (!ibuf) { ok = false; break; } - /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ - if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { - const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; - BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + const char *filepath = ibuf->name; + if (is_tiled) { + iuser.tile = tile->tile_number; + BKE_image_user_file_path(&iuser, ima, tiled_filepath); + filepath = tiled_filepath; + } + else if (is_multiview) { + ImageView *iv = static_cast<ImageView *>(BLI_findlink(&ima->views, view)); + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[view], ""); + } + filepath = iv->filepath; } - ok = ok && image_memorypack_imbuf(ima, ibuf, iv->filepath); + ok = ok && image_memorypack_imbuf(ima, ibuf, view, tile->tile_number, filepath); IMB_freeImBuf(ibuf); } - - ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } - else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); - if (ibuf) { - ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); - IMB_freeImBuf(ibuf); - } - else { - ok = false; - } + if (is_multiview) { + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } if (ok && ima->source == IMA_SRC_GENERATED) { @@ -1284,27 +1286,24 @@ void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) { const int tot_viewfiles = image_num_viewfiles(ima); - if (tot_viewfiles == 1) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); - BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath); - if (imapf->packedfile) { - STRNCPY(imapf->filepath, ima->filepath); - } - else { - BLI_freelinkN(&ima->packedfiles, imapf); - } - } - else { - for (ImageView *iv = static_cast<ImageView *>(ima->views.first); iv; iv = iv->next) { + ImageUser iuser{}; + BKE_imageuser_default(&iuser); + for (int view = 0; view < tot_viewfiles; view++) { + iuser.view = view; + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + iuser.tile = tile->tile_number; + char filepath[FILE_MAX]; + BKE_image_user_file_path(&iuser, ima, filepath); + ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, iv->filepath, basepath); + imapf->packedfile = BKE_packedfile_new(reports, filepath, basepath); + imapf->view = view; + imapf->tile_number = tile->tile_number; if (imapf->packedfile) { - STRNCPY(imapf->filepath, iv->filepath); + STRNCPY(imapf->filepath, filepath); } else { BLI_freelinkN(&ima->packedfiles, imapf); @@ -1323,11 +1322,16 @@ void BKE_image_packfiles_from_mem(ReportList *reports, if (tot_viewfiles != 1) { BKE_report(reports, RPT_ERROR, "Cannot pack multiview images from raw data currently..."); } + else if (ima->source == IMA_SRC_TILED) { + BKE_report(reports, RPT_ERROR, "Cannot pack tiled images from raw data currently..."); + } else { ImagePackedFile *imapf = static_cast<ImagePackedFile *>( MEM_mallocN(sizeof(ImagePackedFile), __func__)); BLI_addtail(&ima->packedfiles, imapf); imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len); + imapf->view = 0; + imapf->tile_number = 1001; STRNCPY(imapf->filepath, ima->filepath); } } @@ -2851,11 +2855,9 @@ static void image_free_tile(Image *ima, ImageTile *tile) } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - ima->gputexture[i][eye][resolution] = nullptr; - } + if (ima->gputexture[i][eye] != nullptr) { + GPU_texture_free(ima->gputexture[i][eye]); + ima->gputexture[i][eye] = nullptr; } } } @@ -2951,8 +2953,9 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) /* try to repack file */ if (BKE_image_has_packedfile(ima)) { const int tot_viewfiles = image_num_viewfiles(ima); + const int tot_files = tot_viewfiles * BLI_listbase_count(&ima->tiles); - if (tot_viewfiles != BLI_listbase_count_at_most(&ima->packedfiles, tot_viewfiles + 1)) { + if (tot_files != BLI_listbase_count_at_most(&ima->packedfiles, tot_files + 1)) { /* in case there are new available files to be loaded */ image_free_packedfiles(ima); BKE_image_packfiles(nullptr, ima, ID_BLEND_PATH(bmain, &ima->id)); @@ -3194,16 +3197,14 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; - } + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; } } BKE_image_partial_update_mark_full_update(ima); @@ -3259,17 +3260,14 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; - } + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = nullptr; } } BKE_image_partial_update_mark_full_update(ima); @@ -3931,18 +3929,23 @@ static ImBuf *load_image_single(Image *ima, int flag = IB_rect | IB_multilayer; *r_cache_ibuf = true; + const int tile_number = image_get_tile_number_from_iuser(ima, iuser); /* is there a PackedFile with this image ? */ if (has_packed && !is_sequence) { - ImagePackedFile *imapf = static_cast<ImagePackedFile *>( - BLI_findlink(&ima->packedfiles, view_id)); - if (imapf->packedfile) { - flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, - imapf->packedfile->size, - flag, - ima->colorspace_settings.name, - "<packed data>"); + flag |= imbuf_alpha_flags_for_image(ima); + + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + if (imapf->view == view_id && imapf->tile_number == tile_number) { + if (imapf->packedfile) { + ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, + imapf->packedfile->size, + flag, + ima->colorspace_settings.name, + "<packed data>"); + } + break; + } } } else { @@ -4001,6 +4004,8 @@ static ImBuf *load_image_single(Image *ima, BLI_addtail(&ima->packedfiles, imapf); STRNCPY(imapf->filepath, filepath); + imapf->view = view_id; + imapf->tile_number = tile_number; imapf->packedfile = BKE_packedfile_new( nullptr, filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); } @@ -5108,7 +5113,7 @@ bool BKE_image_has_packedfile(const Image *ima) return (BLI_listbase_is_empty(&ima->packedfiles) == false); } -bool BKE_image_has_filepath(Image *ima) +bool BKE_image_has_filepath(const Image *ima) { /* This could be improved to detect cases like //../../, currently path * remapping empty file paths empty. */ diff --git a/source/blender/blenkernel/intern/image_format.cc b/source/blender/blenkernel/intern/image_format.cc index 30be1fdaba7..57763e1670f 100644 --- a/source/blender/blenkernel/intern/image_format.cc +++ b/source/blender/blenkernel/intern/image_format.cc @@ -911,6 +911,11 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf) im_format->planes = imbuf->planes; } +bool BKE_image_format_is_byte(const ImageFormatData *imf) +{ + return (imf->depth == R_IMF_CHAN_DEPTH_8) && (BKE_imtype_valid_depths(imf->imtype) & imf->depth); +} + /* Color Management */ void BKE_image_format_color_management_copy(ImageFormatData *imf, const ImageFormatData *imf_src) diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 0d470c5b663..bed79a318e8 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -38,7 +38,6 @@ extern "C" { /* Prototypes. */ static void gpu_free_unused_buffers(); static void image_free_gpu(Image *ima, const bool immediate); -static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); @@ -68,22 +67,19 @@ bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf) /** \name UDIM GPU Texture * \{ */ -static bool is_over_resolution_limit(int w, int h, bool limit_gl_texture_size) +static bool is_over_resolution_limit(int w, int h) { - return (w > GPU_texture_size_with_limit(w, limit_gl_texture_size) || - h > GPU_texture_size_with_limit(h, limit_gl_texture_size)); + return (w > GPU_texture_size_with_limit(w) || h > GPU_texture_size_with_limit(h)); } -static int smaller_power_of_2_limit(int num, bool limit_gl_texture_size) +static int smaller_power_of_2_limit(int num) { - return power_of_2_min_i(GPU_texture_size_with_limit(num, limit_gl_texture_size)); + return power_of_2_min_i(GPU_texture_size_with_limit(num)); } -static GPUTexture *gpu_texture_create_tile_mapping( - Image *ima, const int multiview_eye, const eImageTextureResolution texture_resolution) +static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) { - const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; - GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye][resolution]; + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; if (tilearray == nullptr) { return nullptr; @@ -105,7 +101,7 @@ static GPUTexture *gpu_texture_create_tile_mapping( } LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { int i = tile->tile_number - 1001; - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; data[4 * i] = tile_runtime->tilearray_layer; float *tile_info = &data[4 * width + 4 * i]; @@ -137,12 +133,8 @@ static int compare_packtile(const void *a, const void *b) return tile_a->pack_score < tile_b->pack_score; } -static GPUTexture *gpu_texture_create_tile_array(Image *ima, - ImBuf *main_ibuf, - const eImageTextureResolution texture_resolution) +static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) { - const bool limit_gl_texture_size = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED; - const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; int arraywidth = 0, arrayheight = 0; ListBase boxes = {nullptr}; @@ -158,10 +150,9 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, packtile->boxpack.w = ibuf->x; packtile->boxpack.h = ibuf->y; - if (is_over_resolution_limit( - packtile->boxpack.w, packtile->boxpack.h, limit_gl_texture_size)) { - packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w, limit_gl_texture_size); - packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h, limit_gl_texture_size); + if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) { + packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); + packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); } arraywidth = max_ii(arraywidth, packtile->boxpack.w); arrayheight = max_ii(arrayheight, packtile->boxpack.h); @@ -188,7 +179,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, LISTBASE_FOREACH (PackTile *, packtile, &packed) { ImageTile *tile = packtile->tile; - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; @@ -210,7 +201,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, /* Upload each tile one by one. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int tilelayer = tile_runtime->tilearray_layer; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; @@ -258,33 +249,16 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, /** \name Regular gpu texture * \{ */ -static bool image_max_resolution_texture_fits_in_limited_scale(Image *ima, - eGPUTextureTarget textarget, - const int multiview_eye) -{ - BLI_assert_msg(U.glreslimit != 0, - "limited scale function called without limited scale being set."); - GPUTexture *max_resolution_texture = - ima->gputexture[textarget][multiview_eye][IMA_TEXTURE_RESOLUTION_FULL]; - if (max_resolution_texture && GPU_texture_width(max_resolution_texture) <= U.glreslimit && - GPU_texture_height(max_resolution_texture) <= U.glreslimit) { - return true; - } - return false; -} - static GPUTexture **get_image_gpu_texture_ptr(Image *ima, eGPUTextureTarget textarget, - const int multiview_eye, - const eImageTextureResolution texture_resolution) + const int multiview_eye) { const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); BLI_assert(in_range); BLI_assert(ELEM(multiview_eye, 0, 1)); - const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; if (in_range) { - return &(ima->gputexture[textarget][multiview_eye][resolution]); + return &(ima->gputexture[textarget][multiview_eye]); } return nullptr; } @@ -303,21 +277,6 @@ static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) } } -static void image_update_reusable_textures(Image *ima, - eGPUTextureTarget textarget, - const int multiview_eye) -{ - if ((ima->gpuflag & IMA_GPU_HAS_LIMITED_SCALE_TEXTURES) == 0) { - return; - } - - if (ELEM(textarget, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - if (image_max_resolution_texture_fits_in_limited_scale(ima, textarget, multiview_eye)) { - image_free_gpu_limited_scale(ima); - } - } -} - static void image_gpu_texture_partial_update_changes_available( Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) { @@ -412,14 +371,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (current_view >= 2) { current_view = 0; } - const bool limit_resolution = U.glreslimit != 0 && - ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || - (iuser == nullptr)) && - ((ima->gpuflag & IMA_GPU_REUSE_MAX_RESOLUTION) == 0); - const eImageTextureResolution texture_resolution = limit_resolution ? - IMA_TEXTURE_RESOLUTION_LIMITED : - IMA_TEXTURE_RESOLUTION_FULL; - GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view, texture_resolution); + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); if (*tex) { return *tex; } @@ -443,11 +395,10 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } if (textarget == TEXTARGET_2D_ARRAY) { - *tex = gpu_texture_create_tile_array(ima, ibuf_intern, texture_resolution); + *tex = gpu_texture_create_tile_array(ima, ibuf_intern); } else if (textarget == TEXTARGET_TILE_MAPPING) { - *tex = gpu_texture_create_tile_mapping( - ima, iuser ? iuser->multiview_eye : 0, texture_resolution); + *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); } else { const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); @@ -455,7 +406,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, ibuf_intern); *tex = IMB_create_gpu_texture( - ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied, limit_resolution); + ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied); if (*tex) { GPU_texture_wrap_mode(*tex, true, false); @@ -473,20 +424,6 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } } - switch (texture_resolution) { - case IMA_TEXTURE_RESOLUTION_LIMITED: - ima->gpuflag |= IMA_GPU_HAS_LIMITED_SCALE_TEXTURES; - break; - - case IMA_TEXTURE_RESOLUTION_FULL: - image_update_reusable_textures(ima, textarget, current_view); - break; - - case IMA_TEXTURE_RESOLUTION_LEN: - BLI_assert_unreachable(); - break; - } - if (*tex) { GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); } @@ -558,39 +495,22 @@ static void image_free_gpu(Image *ima, const bool immediate) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye][resolution]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); - } - - ima->gputexture[i][eye][resolution] = nullptr; + if (ima->gputexture[i][eye] != nullptr) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); } - } - } - } - - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); -} -static void image_free_gpu_limited_scale(Image *ima) -{ - const eImageTextureResolution resolution = IMA_TEXTURE_RESOLUTION_LIMITED; - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye][resolution] != nullptr) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - ima->gputexture[i][eye][resolution] = nullptr; + ima->gputexture[i][eye] = nullptr; } } } - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; } void BKE_image_free_gputextures(Image *ima) @@ -767,20 +687,12 @@ static void gpu_texture_update_unscaled(GPUTexture *tex, GPU_unpack_row_length_set(0); } -static void gpu_texture_update_from_ibuf(GPUTexture *tex, - Image *ima, - ImBuf *ibuf, - ImageTile *tile, - int x, - int y, - int w, - int h, - eImageTextureResolution texture_resolution) +static void gpu_texture_update_from_ibuf( + GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) { - const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; bool scaled; if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tilesize = tile_runtime->tilearray_size; scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); } @@ -845,7 +757,7 @@ static void gpu_texture_update_from_ibuf(GPUTexture *tex, if (scaled) { /* Slower update where we first have to scale the input pixels. */ if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int *tilesize = tile_runtime->tilearray_size; int tilelayer = tile_runtime->tilearray_layer; @@ -860,7 +772,7 @@ static void gpu_texture_update_from_ibuf(GPUTexture *tex, else { /* Fast update at same resolution. */ if (tile != nullptr) { - ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + ImageTile_Runtime *tile_runtime = &tile->runtime; int *tileoffset = tile_runtime->tilearray_offset; int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_unscaled( @@ -894,19 +806,16 @@ static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) { const int eye = 0; - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye][resolution]; - eImageTextureResolution texture_resolution = static_cast<eImageTextureResolution>(resolution); - /* Check if we need to update the main gputexture. */ - if (tex != nullptr && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h, texture_resolution); - } + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye]; + /* Check if we need to update the main gputexture. */ + if (tex != nullptr && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h); + } - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]; - if (tex != nullptr) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h, texture_resolution); - } + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye]; + if (tex != nullptr) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); } } @@ -946,11 +855,9 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) for (int a = 0; a < TEXTARGET_COUNT; a++) { if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *tex = ima->gputexture[a][eye][resolution]; - if (tex != nullptr) { - GPU_texture_mipmap_mode(tex, mipmap, true); - } + GPUTexture *tex = ima->gputexture[a][eye]; + if (tex != nullptr) { + GPU_texture_mipmap_mode(tex, mipmap, true); } } } diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 5361f234a63..6bfdaf9b522 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -23,6 +23,7 @@ #include "IMB_openexr.h" #include "BKE_colortools.h" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_image_format.h" #include "BKE_image_save.h" @@ -34,14 +35,217 @@ using blender::Vector; -void BKE_image_save_options_init(ImageSaveOptions *opts, Main *bmain, Scene *scene) +static char imtype_best_depth(ImBuf *ibuf, const char imtype) { + const char depth_ok = BKE_imtype_valid_depths(imtype); + + if (ibuf->rect_float) { + if (depth_ok & R_IMF_CHAN_DEPTH_32) { + return R_IMF_CHAN_DEPTH_32; + } + if (depth_ok & R_IMF_CHAN_DEPTH_24) { + return R_IMF_CHAN_DEPTH_24; + } + if (depth_ok & R_IMF_CHAN_DEPTH_16) { + return R_IMF_CHAN_DEPTH_16; + } + if (depth_ok & R_IMF_CHAN_DEPTH_12) { + return R_IMF_CHAN_DEPTH_12; + } + return R_IMF_CHAN_DEPTH_8; + } + + if (depth_ok & R_IMF_CHAN_DEPTH_8) { + return R_IMF_CHAN_DEPTH_8; + } + if (depth_ok & R_IMF_CHAN_DEPTH_12) { + return R_IMF_CHAN_DEPTH_12; + } + if (depth_ok & R_IMF_CHAN_DEPTH_16) { + return R_IMF_CHAN_DEPTH_16; + } + if (depth_ok & R_IMF_CHAN_DEPTH_24) { + return R_IMF_CHAN_DEPTH_24; + } + if (depth_ok & R_IMF_CHAN_DEPTH_32) { + return R_IMF_CHAN_DEPTH_32; + } + return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */ +} + +bool BKE_image_save_options_init(ImageSaveOptions *opts, + Main *bmain, + Scene *scene, + Image *ima, + ImageUser *iuser, + const bool guess_path, + const bool save_as_render) +{ + /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ + ImageUser save_iuser; + if (iuser == nullptr) { + BKE_imageuser_default(&save_iuser); + iuser = &save_iuser; + iuser->scene = scene; + } + memset(opts, 0, sizeof(*opts)); opts->bmain = bmain; opts->scene = scene; + opts->save_as_render = ima->source == IMA_SRC_VIEWER || save_as_render; BKE_image_format_init(&opts->im_format, false); + + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); + + if (ibuf) { + Scene *scene = opts->scene; + bool is_depth_set = false; + const char *ima_colorspace = ima->colorspace_settings.name; + + if (opts->save_as_render) { + /* Render/compositor output or user chose to save with render settings. */ + BKE_image_format_init_for_write(&opts->im_format, scene, nullptr); + is_depth_set = true; + if (!BKE_image_is_multiview(ima)) { + /* In case multiview is disabled, + * render settings would be invalid for render result in this area. */ + opts->im_format.stereo3d_format = *ima->stereo3d_format; + opts->im_format.views_format = ima->views_format; + } + } + else { + if (ima->source == IMA_SRC_GENERATED) { + opts->im_format.imtype = R_IMF_IMTYPE_PNG; + opts->im_format.compress = ibuf->foptions.quality; + opts->im_format.planes = ibuf->planes; + if (!IMB_colormanagement_space_name_is_data(ima_colorspace)) { + ima_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); + } + } + else { + BKE_image_format_from_imbuf(&opts->im_format, ibuf); + } + + /* use the multiview image settings as the default */ + opts->im_format.stereo3d_format = *ima->stereo3d_format; + opts->im_format.views_format = ima->views_format; + + /* Render output: colorspace from render settings. */ + BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); + } + + /* Default to saving in the same colorspace as the image setting. */ + if (!opts->save_as_render) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, ima_colorspace); + } + + opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; + + if (ibuf->name[0] == '\0' || ima->source == IMA_SRC_TILED) { + BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); + BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + } + else { + BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); + } + + /* sanitize all settings */ + + /* unlikely but just in case */ + if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { + opts->im_format.planes = R_IMF_PLANES_RGBA; + } + + /* depth, account for float buffer and format support */ + if (is_depth_set == false) { + opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype); + } + + /* some formats don't use quality so fallback to scenes quality */ + if (opts->im_format.quality == 0) { + opts->im_format.quality = scene->r.im_format.quality; + } + + /* check for empty path */ + if (guess_path && opts->filepath[0] == 0) { + const bool is_prev_save = !STREQ(G.ima, "//"); + if (opts->save_as_render) { + if (is_prev_save) { + BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); + } + else { + BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath)); + BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); + } + } + else { + BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); + BLI_path_make_safe(opts->filepath); + BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); + } + + /* append UDIM marker if not present */ + if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == nullptr) { + int len = strlen(opts->filepath); + STR_CONCAT(opts->filepath, len, ".<UDIM>"); + } + } + } + + /* Copy for detecting UI changes. */ + opts->prev_save_as_render = opts->save_as_render; + opts->prev_imtype = opts->im_format.imtype; + + BKE_image_release_ibuf(ima, ibuf, lock); + + return (ibuf != nullptr); +} + +void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image) +{ + /* Auto update color space when changing save as render and file type. */ + if (opts->save_as_render) { + if (!opts->prev_save_as_render) { + if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + BKE_image_format_init_for_write(&opts->im_format, opts->scene, nullptr); + } + else { + BKE_image_format_color_management_copy_from_scene(&opts->im_format, opts->scene); + } + } + } + else { + if (opts->prev_save_as_render) { + /* Copy colorspace from image settings. */ + BKE_color_managed_colorspace_settings_copy(&opts->im_format.linear_colorspace_settings, + &image->colorspace_settings); + } + else if (opts->im_format.imtype != opts->prev_imtype && + !IMB_colormanagement_space_name_is_data( + opts->im_format.linear_colorspace_settings.name)) { + const bool linear_float_output = BKE_imtype_requires_linear_float(opts->im_format.imtype); + + /* TODO: detect if the colorspace is linear, not just equal to scene linear. */ + const bool is_linear = IMB_colormanagement_space_name_is_scene_linear( + opts->im_format.linear_colorspace_settings.name); + + /* If changing to a linear float or byte format, ensure we have a compatible color space. */ + if (linear_float_output && !is_linear) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_FLOAT)); + } + else if (!linear_float_output && is_linear) { + STRNCPY(opts->im_format.linear_colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE)); + } + } + } + + opts->prev_save_as_render = opts->save_as_render; + opts->prev_imtype = opts->im_format.imtype; } void BKE_image_save_options_free(ImageSaveOptions *opts) @@ -105,12 +309,17 @@ static void image_save_post(ReportList *reports, BLI_path_rel(ima->filepath, relbase); /* only after saving */ } - ColorManagedColorspaceSettings old_colorspace_settings; - BKE_color_managed_colorspace_settings_copy(&old_colorspace_settings, &ima->colorspace_settings); - IMB_colormanagement_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); - if (!BKE_color_managed_colorspace_settings_equals(&old_colorspace_settings, - &ima->colorspace_settings)) { - *r_colorspace_changed = true; + /* Update image file color space when saving to another color space. */ + const bool linear_float_output = BKE_imtype_requires_linear_float(opts->im_format.imtype); + + if (!opts->save_as_render || linear_float_output) { + if (opts->im_format.linear_colorspace_settings.name[0] && + !BKE_color_managed_colorspace_settings_equals( + &ima->colorspace_settings, &opts->im_format.linear_colorspace_settings)) { + BKE_color_managed_colorspace_settings_copy(&ima->colorspace_settings, + &opts->im_format.linear_colorspace_settings); + *r_colorspace_changed = true; + } } } @@ -176,12 +385,12 @@ static bool image_save_single(ReportList *reports, /* we need renderresult for exr and rendered multiview */ rr = BKE_image_acquire_renderresult(opts->scene, ima); - bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : - BLI_listbase_count_at_most(&ima->views, 2) < 2; - bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && - RE_HasFloatPixels(rr); - bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); - int layer = (iuser && !is_multilayer) ? iuser->layer : -1; + const bool is_mono = rr ? BLI_listbase_count_at_most(&rr->views, 2) < 2 : + BLI_listbase_count_at_most(&ima->views, 2) < 2; + const bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + RE_HasFloatPixels(rr); + const bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER); + const int layer = (iuser && !is_multilayer) ? iuser->layer : -1; /* error handling */ if (rr == nullptr) { @@ -366,7 +575,6 @@ static bool image_save_single(ReportList *reports, colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf); BKE_image_format_to_imbuf(colormanaged_ibuf, imf); - IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); /* duplicate buffer to prevent locker issue when using render result */ ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); @@ -401,8 +609,13 @@ static bool image_save_single(ReportList *reports, bool BKE_image_save( ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) { + /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ ImageUser save_iuser; - BKE_imageuser_default(&save_iuser); + if (iuser == nullptr) { + BKE_imageuser_default(&save_iuser); + iuser = &save_iuser; + iuser->scene = opts->scene; + } bool colorspace_changed = false; @@ -419,12 +632,6 @@ bool BKE_image_save( opts->filepath); return false; } - - /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. - */ - if (iuser == nullptr) { - iuser = &save_iuser; - } } /* Save images */ @@ -787,7 +994,6 @@ bool BKE_image_render_write(ReportList *reports, int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id); IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); } ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 27427b1fb44..90a4853fd3e 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -263,7 +263,6 @@ void id_us_ensure_real(ID *id) "ID user count error: %s (from '%s')", id->name, id->lib ? id->lib->filepath_abs : "[Main]"); - BLI_assert(0); } id->us = limit + 1; id->tag |= LIB_TAG_EXTRAUSER_SET; @@ -312,7 +311,7 @@ void id_us_min(ID *id) const int limit = ID_FAKE_USERS(id); if (id->us <= limit) { - if (GS(id->name) != ID_IP) { + if (!ID_TYPE_IS_DEPRECATED(GS(id->name))) { /* Do not assert on deprecated ID types, we cannot really ensure that their ID refcounting * is valid... */ CLOG_ERROR(&LOG, @@ -321,7 +320,6 @@ void id_us_min(ID *id) id->lib ? id->lib->filepath_abs : "[Main]", id->us, limit); - BLI_assert(0); } id->us = limit; } @@ -592,11 +590,9 @@ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data) bool BKE_id_copy_is_allowed(const ID *id) { -#define LIB_ID_TYPES_NOCOPY \ - ID_LI, ID_SCR, ID_WM, ID_WS, /* Not supported */ \ - ID_IP /* Deprecated */ +#define LIB_ID_TYPES_NOCOPY ID_LI, ID_SCR, ID_WM, ID_WS /* Not supported */ - return !ELEM(GS(id->name), LIB_ID_TYPES_NOCOPY); + return !ID_TYPE_IS_DEPRECATED(GS(id->name)) && !ELEM(GS(id->name), LIB_ID_TYPES_NOCOPY); #undef LIB_ID_TYPES_NOCOPY } @@ -2165,7 +2161,7 @@ bool BKE_id_can_be_asset(const ID *id) BKE_idtype_idcode_is_linkable(GS(id->name)); } -bool BKE_id_is_editable(Main *bmain, ID *id) +bool BKE_id_is_editable(const Main *bmain, const ID *id) { return !(ID_IS_LINKED(id) || BKE_lib_override_library_is_system_defined(bmain, id)); } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.cc index 768ac4f606f..168feebedec 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -5,8 +5,8 @@ * \ingroup bke */ -#include <stdlib.h> -#include <string.h> +#include <cstdlib> +#include <cstring> #include "CLG_log.h" @@ -72,17 +72,36 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op); static void lib_override_library_property_operation_clear( IDOverrideLibraryPropertyOperation *opop); +/** Helper to preserve Pose mode on override objects. + * A bit annoying to have this special case, but not much to be done here currently, since the + * matching RNA property is read-only. */ +BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src) +{ + if (GS(id_src->name) == ID_OB && GS(id_dst->name) == ID_OB) { + Object *ob_src = (Object *)id_src; + Object *ob_dst = (Object *)id_dst; + if (ob_src->type == OB_ARMATURE && (ob_src->mode & OB_MODE_POSE) != 0) { + ob_dst->restore_mode = ob_dst->mode; + ob_dst->mode |= OB_MODE_POSE; + } + } +} + /** 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, ID **r_owner_id) +BLI_INLINE const IDOverrideLibrary *lib_override_get(const Main *bmain, + const ID *id, + const ID **r_owner_id) { - if (r_owner_id != NULL) { + if (r_owner_id != nullptr) { *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) { - ID *owner_id = id_type->owner_get(bmain, id); - if (r_owner_id != NULL) { + if (id_type->owner_get != nullptr) { + /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const + * is okay. */ + const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id)); + if (r_owner_id != nullptr) { *r_owner_id = owner_id; } return owner_id->override_library; @@ -92,21 +111,31 @@ BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner return id->override_library; } +BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id) +{ + /* Reuse the implementation of the const access function, which does not change the arguments. + * Add const explicitly to make it clear to the compiler to avoid just calling this function. */ + return const_cast<IDOverrideLibrary *>(lib_override_get(const_cast<const Main *>(bmain), + const_cast<const ID *>(id), + const_cast<const ID **>(r_owner_id))); +} + IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id) { - /* If reference_id is NULL, we are creating an override template for purely local data. + /* If reference_id is nullptr, we are creating an override template for purely local data. * Else, reference *must* be linked data. */ - BLI_assert(reference_id == NULL || ID_IS_LINKED(reference_id)); - BLI_assert(local_id->override_library == NULL); + BLI_assert(reference_id == nullptr || ID_IS_LINKED(reference_id)); + BLI_assert(local_id->override_library == nullptr); ID *ancestor_id; - for (ancestor_id = reference_id; ancestor_id != NULL && ancestor_id->override_library != NULL && - ancestor_id->override_library->reference != NULL; + for (ancestor_id = reference_id; + ancestor_id != nullptr && ancestor_id->override_library != nullptr && + ancestor_id->override_library->reference != nullptr; ancestor_id = ancestor_id->override_library->reference) { /* pass */ } - if (ancestor_id != NULL && ancestor_id->override_library != NULL) { + if (ancestor_id != nullptr && ancestor_id->override_library != nullptr) { /* Original ID has a template, use it! */ BKE_lib_override_library_copy(local_id, ancestor_id, true); if (local_id->override_library->reference != reference_id) { @@ -118,7 +147,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id) } /* Else, generate new empty override. */ - local_id->override_library = MEM_callocN(sizeof(*local_id->override_library), __func__); + local_id->override_library = MEM_cnew<IDOverrideLibrary>(__func__); local_id->override_library->reference = reference_id; id_us_plus(local_id->override_library->reference); local_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; @@ -133,20 +162,20 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f { BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id) || ID_IS_OVERRIDE_LIBRARY_TEMPLATE(src_id)); - if (dst_id->override_library != NULL) { - if (src_id->override_library == NULL) { + if (dst_id->override_library != nullptr) { + if (src_id->override_library == nullptr) { BKE_lib_override_library_free(&dst_id->override_library, true); return; } BKE_lib_override_library_clear(dst_id->override_library, true); } - else if (src_id->override_library == NULL) { + else if (src_id->override_library == nullptr) { /* Virtual overrides of embedded data does not require any extra work. */ return; } else { - BKE_lib_override_library_init(dst_id, NULL); + BKE_lib_override_library_init(dst_id, nullptr); } /* If source is already overriding data, we copy it but reuse its reference for dest ID. @@ -162,8 +191,10 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f if (do_full_copy) { BLI_duplicatelist(&dst_id->override_library->properties, &src_id->override_library->properties); - for (IDOverrideLibraryProperty *op_dst = dst_id->override_library->properties.first, - *op_src = src_id->override_library->properties.first; + for (IDOverrideLibraryProperty *op_dst = static_cast<IDOverrideLibraryProperty *>( + dst_id->override_library->properties.first), + *op_src = static_cast<IDOverrideLibraryProperty *>( + src_id->override_library->properties.first); op_dst; op_dst = op_dst->next, op_src = op_src->next) { lib_override_library_property_copy(op_dst, op_src); @@ -175,10 +206,10 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f void BKE_lib_override_library_clear(IDOverrideLibrary *override, const bool do_id_user) { - BLI_assert(override != NULL); + BLI_assert(override != nullptr); - if (!ELEM(NULL, override->runtime, override->runtime->rna_path_to_override_properties)) { - BLI_ghash_clear(override->runtime->rna_path_to_override_properties, NULL, NULL); + if (!ELEM(nullptr, override->runtime, override->runtime->rna_path_to_override_properties)) { + BLI_ghash_clear(override->runtime->rna_path_to_override_properties, nullptr, nullptr); } LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) { @@ -192,20 +223,20 @@ void BKE_lib_override_library_clear(IDOverrideLibrary *override, const bool do_i } } -void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user) +void BKE_lib_override_library_free(IDOverrideLibrary **override, const bool do_id_user) { - BLI_assert(*override != NULL); + BLI_assert(*override != nullptr); - if ((*override)->runtime != NULL) { - if ((*override)->runtime->rna_path_to_override_properties != NULL) { - BLI_ghash_free((*override)->runtime->rna_path_to_override_properties, NULL, NULL); + if ((*override)->runtime != nullptr) { + if ((*override)->runtime->rna_path_to_override_properties != nullptr) { + BLI_ghash_free((*override)->runtime->rna_path_to_override_properties, nullptr, nullptr); } MEM_SAFE_FREE((*override)->runtime); } BKE_lib_override_library_clear(*override, do_id_user); MEM_freeN(*override); - *override = NULL; + *override = nullptr; } static ID *lib_override_library_create_from(Main *bmain, @@ -217,12 +248,12 @@ static ID *lib_override_library_create_from(Main *bmain, * override template, or already an override of some other ref data). */ ID *local_id = BKE_id_copy_ex(bmain, reference_id, - NULL, + nullptr, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE | lib_id_copy_flags); - if (local_id == NULL) { - return NULL; + if (local_id == nullptr) { + return nullptr; } id_us_min(local_id); @@ -238,9 +269,9 @@ static ID *lib_override_library_create_from(Main *bmain, * data-blocks, just like root node trees or master collections. Therefore, we never need to * create overrides for them. We need a way to mark them as overrides though. */ Key *reference_key; - if ((reference_key = BKE_key_from_id(reference_id)) != NULL) { + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { Key *local_key = BKE_key_from_id(local_id); - BLI_assert(local_key != NULL); + BLI_assert(local_key != nullptr); local_key->id.flag |= LIB_EMBEDDED_DATA_LIB_OVERRIDE; } @@ -249,7 +280,7 @@ static ID *lib_override_library_create_from(Main *bmain, /* TODO: This could be simplified by storing a flag in #IDOverrideLibrary * during the diffing process? */ -bool BKE_lib_override_library_is_user_edited(struct ID *id) +bool BKE_lib_override_library_is_user_edited(const ID *id) { if (!ID_IS_OVERRIDE_LIBRARY(id)) { @@ -263,8 +294,8 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } - LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { - LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + LISTBASE_FOREACH (const IDOverrideLibraryProperty *, op, &id->override_library->properties) { + LISTBASE_FOREACH (const IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { continue; } @@ -279,11 +310,11 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } -bool BKE_lib_override_library_is_system_defined(Main *bmain, ID *id) +bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id) { if (ID_IS_OVERRIDE_LIBRARY(id)) { - ID *override_owner_id; + const ID *override_owner_id; lib_override_get(bmain, id, &override_owner_id); return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) != 0; @@ -295,9 +326,9 @@ static int foreachid_is_hierarchy_leaf_fn(LibraryIDLinkCallbackData *cb_data) { ID *id_owner = cb_data->id_owner; ID *id = *cb_data->id_pointer; - bool *is_leaf = cb_data->user_data; + bool *is_leaf = static_cast<bool *>(cb_data->user_data); - if (id != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && + if (id != nullptr && ID_IS_OVERRIDE_LIBRARY_REAL(id) && id->override_library->hierarchy_root == id_owner->override_library->hierarchy_root) { *is_leaf = false; return IDWALK_RET_STOP_ITER; @@ -321,10 +352,10 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, ID *reference_id, const bool do_tagged_remap) { - BLI_assert(reference_id != NULL); + BLI_assert(reference_id != nullptr); BLI_assert(ID_IS_LINKED(reference_id)); - ID *local_id = lib_override_library_create_from(bmain, NULL, reference_id, 0); + ID *local_id = lib_override_library_create_from(bmain, nullptr, reference_id, 0); /* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant * mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies. * Ref T94650. */ @@ -333,10 +364,10 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, local_id->override_library->hierarchy_root = local_id; if (do_tagged_remap) { - Key *reference_key, *local_key = NULL; - if ((reference_key = BKE_key_from_id(reference_id)) != NULL) { + Key *reference_key, *local_key = nullptr; + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { local_key = BKE_key_from_id(local_id); - BLI_assert(local_key != NULL); + BLI_assert(local_key != nullptr); } ID *other_id; @@ -349,7 +380,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, reference_id, local_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); - if (reference_key != NULL) { + if (reference_key != nullptr) { BKE_libblock_relink_ex(bmain, other_id, &reference_key->id, @@ -383,18 +414,19 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, const ID *id_root_reference, ID *id_hierarchy_root, const ID *id_hierarchy_root_reference, - const bool do_no_main) + const bool do_no_main, + const bool do_fully_editable) { - BLI_assert(id_root_reference != NULL && ID_IS_LINKED(id_root_reference)); + BLI_assert(id_root_reference != nullptr && 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 || + BLI_assert(id_hierarchy_root != nullptr || id_hierarchy_root_reference != nullptr || (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 + /* At least one of the hierarchy root pointers must be nullptr, passing both is useless and can * create confusion. */ - BLI_assert(ELEM(NULL, id_hierarchy_root, id_hierarchy_root_reference)); + BLI_assert(ELEM(nullptr, id_hierarchy_root, id_hierarchy_root_reference)); - if (id_hierarchy_root != NULL) { + if (id_hierarchy_root != nullptr) { /* 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) && @@ -407,7 +439,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, lib_override_prefill_newid_from_existing_overrides(bmain, id_hierarchy_root); } } - if (!ELEM(id_hierarchy_root_reference, NULL, id_root_reference)) { + if (!ELEM(id_hierarchy_root_reference, nullptr, 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 && @@ -419,14 +451,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, ID *reference_id; bool success = true; - ListBase todo_ids = {NULL}; + ListBase todo_ids = {nullptr}; LinkData *todo_id_iter; /* Get all IDs we want to override. */ FOREACH_MAIN_ID_BEGIN (bmain, reference_id) { if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library && BKE_idtype_idcode_is_linkable(GS(reference_id->name))) { - todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__); + todo_id_iter = MEM_cnew<LinkData>(__func__); todo_id_iter->data = reference_id; BLI_addtail(&todo_ids, todo_id_iter); } @@ -434,31 +466,35 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, FOREACH_MAIN_ID_END; /* Override the IDs. */ - for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { - reference_id = todo_id_iter->data; + for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr; + todo_id_iter = todo_id_iter->next) { + reference_id = static_cast<ID *>(todo_id_iter->data); /* If `newid` is already set, assume it has been handled by calling code. * Only current use case: re-using proxy ID when converting to liboverride. */ - if (reference_id->newid == NULL) { + if (reference_id->newid == nullptr) { /* NOTE: `no main` case is used during resync procedure, to support recursive resync. * This requires extra care further down the resync process, * see: #BKE_lib_override_library_resync. */ reference_id->newid = lib_override_library_create_from( bmain, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0); - if (reference_id->newid == NULL) { + if (reference_id->newid == nullptr) { success = false; break; } + if (do_fully_editable) { + reference_id->newid->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } } /* We also tag the new IDs so that in next step we can remap their pointers too. */ reference_id->newid->tag |= LIB_TAG_DOIT; Key *reference_key; - if ((reference_key = BKE_key_from_id(reference_id)) != NULL) { + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { reference_key->id.tag |= LIB_TAG_DOIT; Key *local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != NULL); + BLI_assert(local_key != nullptr); reference_key->id.newid = &local_key->id; /* We also tag the new IDs so that in next step we can remap their pointers too. */ local_key->id.tag |= LIB_TAG_DOIT; @@ -468,17 +504,17 @@ 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_hierarchy_root_reference != NULL) { + if (id_hierarchy_root_reference != nullptr) { id_hierarchy_root = id_hierarchy_root_reference->newid; } - else if (id_root_reference->newid != NULL && - (id_hierarchy_root == NULL || + else if (id_root_reference->newid != nullptr && + (id_hierarchy_root == nullptr || id_hierarchy_root->override_library->reference == id_root_reference)) { id_hierarchy_root = id_root_reference->newid; } - BLI_assert(id_hierarchy_root != NULL); + BLI_assert(id_hierarchy_root != nullptr); - LinkNode *relinked_ids = NULL; + LinkNode *relinked_ids = nullptr; /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ ID *id; @@ -486,14 +522,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, ID *other_id; /* In case we created new overrides as 'no main', they are not accessible directly in this * loop, but we can get to them through their reference's `newid` pointer. */ - if (do_no_main && id->lib == id_root_reference->lib && id->newid != NULL) { + if (do_no_main && id->lib == id_root_reference->lib && id->newid != nullptr) { other_id = id->newid; /* Otherwise we cannot properly distinguish between IDs that are actually from the * linked library (and should not be remapped), and IDs that are overrides re-generated * from the reference from the linked library, and must therefore be remapped. * * This is reset afterwards at the end of this loop. */ - other_id->lib = NULL; + other_id->lib = nullptr; } else { other_id = id; @@ -510,12 +546,13 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, } FOREACH_MAIN_ID_END; - struct IDRemapper *id_remapper = BKE_id_remapper_create(); - for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { - reference_id = todo_id_iter->data; + IDRemapper *id_remapper = BKE_id_remapper_create(); + for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr; + todo_id_iter = todo_id_iter->next) { + reference_id = static_cast<ID *>(todo_id_iter->data); ID *local_id = reference_id->newid; - if (local_id == NULL) { + if (local_id == nullptr) { continue; } @@ -523,10 +560,10 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, BKE_id_remapper_add(id_remapper, reference_id, local_id); - Key *reference_key, *local_key = NULL; - if ((reference_key = BKE_key_from_id(reference_id)) != NULL) { + Key *reference_key, *local_key = nullptr; + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != NULL); + BLI_assert(local_key != nullptr); BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); } @@ -539,14 +576,15 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT); BKE_id_remapper_free(id_remapper); - BLI_linklist_free(relinked_ids, NULL); + BLI_linklist_free(relinked_ids, nullptr); } else { /* We need to cleanup potentially already created data. */ - for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { - reference_id = todo_id_iter->data; + for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr; + todo_id_iter = todo_id_iter->next) { + reference_id = static_cast<ID *>(todo_id_iter->data); BKE_id_delete(bmain, reference_id->newid); - reference_id->newid = NULL; + reference_id->newid = nullptr; } } @@ -555,7 +593,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, return success; } -typedef struct LibOverrideGroupTagData { +struct LibOverrideGroupTagData { Main *bmain; Scene *scene; ID *id_root; @@ -571,7 +609,7 @@ typedef struct LibOverrideGroupTagData { * Avoids calling #BKE_collection_object_find over and over, this function is very expansive. */ GHash *linked_object_to_instantiating_collections; MemArena *mem_arena; -} LibOverrideGroupTagData; +}; static void lib_override_group_tag_data_object_to_collection_init_collection_process( LibOverrideGroupTagData *data, Collection *collection) @@ -586,8 +624,8 @@ static void lib_override_group_tag_data_object_to_collection_init_collection_pro if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections, ob, (void ***)&collections_linkedlist_p)) { - *collections_linkedlist_p = BLI_memarena_calloc(data->mem_arena, - sizeof(**collections_linkedlist_p)); + *collections_linkedlist_p = static_cast<LinkNodePair *>( + BLI_memarena_calloc(data->mem_arena, sizeof(**collections_linkedlist_p))); } BLI_linklist_append_arena(*collections_linkedlist_p, collection, data->mem_arena); } @@ -604,7 +642,7 @@ static void lib_override_group_tag_data_object_to_collection_init(LibOverrideGro data->linked_object_to_instantiating_collections = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); - if (data->scene != NULL) { + if (data->scene != nullptr) { lib_override_group_tag_data_object_to_collection_init_collection_process( data, data->scene->master_collection); } @@ -615,7 +653,7 @@ static void lib_override_group_tag_data_object_to_collection_init(LibOverrideGro static void lib_override_group_tag_data_clear(LibOverrideGroupTagData *data) { - BLI_ghash_free(data->linked_object_to_instantiating_collections, NULL, NULL); + BLI_ghash_free(data->linked_object_to_instantiating_collections, nullptr, nullptr); BLI_memarena_free(data->mem_arena); memset(data, 0, sizeof(*data)); } @@ -632,8 +670,9 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa ID *id = data->id_root; const bool is_override = data->is_override; - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { /* This ID has already been processed. */ @@ -643,7 +682,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa * relationship hierarchy. */ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as @@ -652,7 +691,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (to_id == NULL || to_id->lib != id->lib || + if (to_id == nullptr || to_id->lib != id->lib || (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { /* IDs from different libraries, or non-override IDs in case we are processing overrides, are * both barriers of dependency. */ @@ -677,9 +716,9 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat const uint tag = data->tag; const uint missing_tag = data->missing_tag; - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, - id_owner); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner)); + BLI_assert(entry != nullptr); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { /* This ID has already been processed. */ @@ -689,7 +728,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat * relationship hierarchy. */ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships as actual dependencies. */ @@ -697,7 +736,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat } ID *to_id = *to_id_entry->id_pointer.to; - if (ELEM(to_id, NULL, id_owner)) { + if (ELEM(to_id, nullptr, id_owner)) { continue; } /* We only consider IDs from the same library. */ @@ -732,10 +771,12 @@ static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursiv * is not usable here, as it may have become invalid from some previous operation and it should * not be updated here. So instead only use collections' reliable 'raw' data to check if some * object in the hierarchy of the given collection is still tagged for override. */ - for (CollectionObject *collection_object = collection->gobject.first; collection_object != NULL; + for (CollectionObject *collection_object = + static_cast<CollectionObject *>(collection->gobject.first); + collection_object != nullptr; collection_object = collection_object->next) { Object *object = collection_object->ob; - if (object == NULL) { + if (object == nullptr) { continue; } if ((object->id.tag & data->tag) != 0) { @@ -743,7 +784,9 @@ static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursiv } } - for (CollectionChild *collection_child = collection->children.first; collection_child != NULL; + for (CollectionChild *collection_child = + static_cast<CollectionChild *>(collection->children.first); + collection_child != nullptr; collection_child = collection_child->next) { if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive( data, collection_child->collection)) { @@ -762,9 +805,11 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr /* Remove (untag) bone shape objects, they shall never need to be to directly/explicitly * overridden. */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->type == OB_ARMATURE && ob->pose != NULL && (ob->id.tag & data->tag)) { - for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) { - if (pchan->custom != NULL && &pchan->custom->id != id_root) { + if (ob->type == OB_ARMATURE && ob->pose != nullptr && (ob->id.tag & data->tag)) { + for (bPoseChannel *pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first); + pchan != nullptr; + pchan = pchan->next) { + if (pchan->custom != nullptr && &pchan->custom->id != id_root) { pchan->custom->id.tag &= ~data->tag; } } @@ -841,17 +886,18 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } LISTBASE_FOREACH (Object *, ob, &bmain->objects) { if (ID_IS_LINKED(ob) && (ob->id.tag & data->tag) != 0) { - Collection *instantiating_collection = NULL; - Collection *instantiating_collection_override_candidate = NULL; + Collection *instantiating_collection = nullptr; + Collection *instantiating_collection_override_candidate = nullptr; /* Loop over all collections instantiating the object, if we already have a 'locale' one we * have nothing to do, otherwise try to find a 'linked' one that we can override too. */ - LinkNodePair *instantiating_collection_linklist = BLI_ghash_lookup( - data->linked_object_to_instantiating_collections, ob); - if (instantiating_collection_linklist != NULL) { + LinkNodePair *instantiating_collection_linklist = static_cast<LinkNodePair *>( + BLI_ghash_lookup(data->linked_object_to_instantiating_collections, ob)); + if (instantiating_collection_linklist != nullptr) { for (LinkNode *instantiating_collection_linknode = instantiating_collection_linklist->list; - instantiating_collection_linknode != NULL; + instantiating_collection_linknode != nullptr; instantiating_collection_linknode = instantiating_collection_linknode->next) { - instantiating_collection = instantiating_collection_linknode->link; + instantiating_collection = static_cast<Collection *>( + instantiating_collection_linknode->link); if (!ID_IS_LINKED(instantiating_collection)) { /* There is a local collection instantiating the linked object to override, nothing * else to be done here. */ @@ -863,12 +909,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) break; } instantiating_collection_override_candidate = instantiating_collection; - instantiating_collection = NULL; + instantiating_collection = nullptr; } } - if (instantiating_collection == NULL && - instantiating_collection_override_candidate != NULL) { + if (instantiating_collection == nullptr && + instantiating_collection_override_candidate != nullptr) { if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) { instantiating_collection_override_candidate->id.tag |= data->missing_tag; } @@ -897,9 +943,9 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * const uint tag = data->tag; const uint missing_tag = data->missing_tag; - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, - id_owner); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner)); + BLI_assert(entry != nullptr); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { /* This ID has already been processed. */ @@ -909,7 +955,7 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * * relationship hierarchy. */ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships as actual dependencies. */ @@ -917,7 +963,7 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * } ID *to_id = *to_id_entry->id_pointer.to; - if (ELEM(to_id, NULL, id_owner)) { + if (ELEM(to_id, nullptr, id_owner)) { continue; } /* Different libraries or different hierarchy roots are break points in override hierarchies. @@ -930,8 +976,8 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * continue; } - Library *reference_lib = lib_override_get(bmain, id_owner, NULL)->reference->lib; - ID *to_id_reference = lib_override_get(bmain, to_id, NULL)->reference; + const Library *reference_lib = lib_override_get(bmain, id_owner, nullptr)->reference->lib; + const ID *to_id_reference = lib_override_get(bmain, to_id, nullptr)->reference; if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; @@ -959,7 +1005,7 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) BLI_assert(data->is_override); ID *id_hierarchy_root = data->hierarchy_root_id; - BLI_assert(id_hierarchy_root != NULL); + BLI_assert(id_hierarchy_root != nullptr); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root)); UNUSED_VARS_NDEBUG(id_hierarchy_root); @@ -978,16 +1024,18 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, Library *owner_library, ID *id_root_reference, - ID *id_hierarchy_root_reference) + ID *id_hierarchy_root_reference, + const bool do_fully_editable) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = {.bmain = bmain, - .scene = scene, - .id_root = id_root_reference, - .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING, - .is_override = false, - .is_resync = false}; + LibOverrideGroupTagData data{}; + data.bmain = bmain; + data.scene = scene; + data.id_root = id_root_reference; + data.tag = LIB_TAG_DOIT; + data.missing_tag = LIB_TAG_MISSING; + data.is_override = false; + data.is_resync = false; lib_override_group_tag_data_object_to_collection_init(&data); lib_override_linked_group_tag(&data); @@ -1002,12 +1050,22 @@ static bool lib_override_library_create_do(Main *bmain, 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); + success = BKE_lib_override_library_create_from_tag(bmain, + owner_library, + id_root_reference, + id_hierarchy_root_reference, + nullptr, + false, + do_fully_editable); } else { - success = BKE_lib_override_library_create_from_tag( - bmain, owner_library, id_root_reference, NULL, id_hierarchy_root_reference, false); + success = BKE_lib_override_library_create_from_tag(bmain, + owner_library, + id_root_reference, + nullptr, + id_hierarchy_root_reference, + false, + do_fully_editable); } return success; @@ -1034,25 +1092,25 @@ static void lib_override_library_create_post_process(Main *bmain, /* We create a set of all objects referenced into the scene by its hierarchy of collections. * NOTE: This is different that the list of bases, since objects in excluded collections etc. * won't have a base, but are still considered as instanced from our point of view. */ - GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, NULL); + GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, nullptr); /* Instantiating the root collection or object should never be needed in resync case, since the * old override would be remapped to the new one. */ - if (!is_resync && id_root != NULL && id_root->newid != NULL && + if (!is_resync && id_root != nullptr && id_root->newid != nullptr && (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) { switch (GS(id_root->name)) { case ID_GR: { - Object *ob_reference = id_instance_hint != NULL && GS(id_instance_hint->name) == ID_OB ? + Object *ob_reference = id_instance_hint != nullptr && GS(id_instance_hint->name) == ID_OB ? (Object *)id_instance_hint : - NULL; + nullptr; Collection *collection_new = ((Collection *)id_root->newid); if (is_resync && BKE_collection_is_in_scene(collection_new)) { break; } - if (ob_reference != NULL) { + if (ob_reference != nullptr) { BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); } - else if (id_instance_hint != NULL) { + else if (id_instance_hint != nullptr) { BLI_assert(GS(id_instance_hint->name) == ID_GR); BKE_collection_add_from_collection( bmain, scene, ((Collection *)id_instance_hint), collection_new); @@ -1069,7 +1127,7 @@ static void lib_override_library_create_post_process(Main *bmain, } case ID_OB: { Object *ob_new = (Object *)id_root->newid; - if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { + if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) { BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new); all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene); } @@ -1084,16 +1142,16 @@ static void lib_override_library_create_post_process(Main *bmain, Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { Object *ob_new = (Object *)ob->id.newid; - if (ob_new == NULL || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) { + if (ob_new == nullptr || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) { continue; } - BLI_assert(ob_new->id.override_library != NULL && + BLI_assert(ob_new->id.override_library != nullptr && ob_new->id.override_library->reference == &ob->id); - if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) { - if (id_root != NULL && default_instantiating_collection == NULL) { - ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; + if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) { + if (id_root != nullptr && default_instantiating_collection == nullptr) { + ID *id_ref = id_root->newid != nullptr ? id_root->newid : id_root; switch (GS(id_ref->name)) { case ID_GR: { /* Adding the object to a specific collection outside of the root overridden one is a @@ -1106,7 +1164,8 @@ static void lib_override_library_create_post_process(Main *bmain, if (ID_REAL_USERS(ob_new) != 0) { continue; } - default_instantiating_collection = BKE_id_new(bmain, ID_GR, "OVERRIDE_HIDDEN"); + default_instantiating_collection = static_cast<Collection *>( + BKE_id_new(bmain, ID_GR, "OVERRIDE_HIDDEN")); id_us_min(&default_instantiating_collection->id); /* Hide the collection from viewport and render. */ default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT | @@ -1119,7 +1178,7 @@ static void lib_override_library_create_post_process(Main *bmain, Object *ob_ref = (Object *)id_ref; LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob_ref) && - (view_layer != NULL ? + (view_layer != nullptr ? BKE_view_layer_has_collection(view_layer, collection) : BKE_collection_has_collection(scene->master_collection, collection)) && !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { @@ -1132,7 +1191,7 @@ static void lib_override_library_create_post_process(Main *bmain, break; } } - if (default_instantiating_collection == NULL) { + if (default_instantiating_collection == nullptr) { default_instantiating_collection = scene->master_collection; } @@ -1141,8 +1200,9 @@ static void lib_override_library_create_post_process(Main *bmain, } } - if (id_root != NULL && !ELEM(default_instantiating_collection, NULL, scene->master_collection)) { - ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; + if (id_root != nullptr && + !ELEM(default_instantiating_collection, nullptr, scene->master_collection)) { + ID *id_ref = id_root->newid != nullptr ? id_root->newid : id_root; switch (GS(id_ref->name)) { case ID_GR: BKE_collection_add_from_collection( @@ -1150,12 +1210,13 @@ static void lib_override_library_create_post_process(Main *bmain, break; default: /* Add to master collection. */ - BKE_collection_add_from_collection(bmain, scene, NULL, default_instantiating_collection); + BKE_collection_add_from_collection( + bmain, scene, nullptr, default_instantiating_collection); break; } } - BLI_gset_free(all_objects_in_scene, NULL); + BLI_gset_free(all_objects_in_scene, nullptr); } bool BKE_lib_override_library_create(Main *bmain, @@ -1165,29 +1226,40 @@ bool BKE_lib_override_library_create(Main *bmain, ID *id_root_reference, ID *id_hierarchy_root_reference, ID *id_instance_hint, - ID **r_id_root_override) + ID **r_id_root_override, + const bool do_fully_editable) { - if (r_id_root_override != NULL) { - *r_id_root_override = NULL; + if (r_id_root_override != nullptr) { + *r_id_root_override = nullptr; } - if (id_hierarchy_root_reference == NULL) { + if (id_hierarchy_root_reference == nullptr) { 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); + const bool success = lib_override_library_create_do(bmain, + scene, + owner_library, + id_root_reference, + id_hierarchy_root_reference, + do_fully_editable); if (!success) { return success; } - if (r_id_root_override != NULL) { + if (r_id_root_override != nullptr) { *r_id_root_override = id_root_reference->newid; } - lib_override_library_create_post_process( - bmain, scene, view_layer, owner_library, id_root_reference, id_instance_hint, NULL, false); + lib_override_library_create_post_process(bmain, + scene, + view_layer, + owner_library, + id_root_reference, + id_instance_hint, + nullptr, + false); /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -1199,7 +1271,7 @@ bool BKE_lib_override_library_create(Main *bmain, return success; } -bool BKE_lib_override_library_template_create(struct ID *id) +bool BKE_lib_override_library_template_create(ID *id) { if (ID_IS_LINKED(id)) { return false; @@ -1208,7 +1280,7 @@ bool BKE_lib_override_library_template_create(struct ID *id) return false; } - BKE_lib_override_library_init(id, NULL); + BKE_lib_override_library_init(id, nullptr); return true; } @@ -1219,17 +1291,17 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int "Levels of dependency relationships between library overrides IDs is way too high, " "skipping further processing loops (involves at least '%s')", id->name); - BLI_assert(0); - return NULL; + return nullptr; } if (!ID_IS_OVERRIDE_LIBRARY(id)) { - BLI_assert(0); - return NULL; + BLI_assert_unreachable(); + return nullptr; } - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { @@ -1251,7 +1323,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int int best_level_candidate = curr_level; ID *best_root_id_candidate = id; - for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != NULL; + for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr; from_id_entry = from_id_entry->next) { if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships as actual dependencies. */ @@ -1259,7 +1331,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int } ID *from_id = from_id_entry->id_pointer.from; - if (ELEM(from_id, NULL, id)) { + if (ELEM(from_id, nullptr, id)) { continue; } if (!ID_IS_OVERRIDE_LIBRARY(from_id) || (from_id->lib != id->lib)) { @@ -1270,7 +1342,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int /* Recursively process the parent. */ ID *root_id_candidate = lib_override_root_find( bmain, from_id, curr_level + 1, &level_candidate); - if (level_candidate > best_level_candidate && root_id_candidate != NULL) { + if (level_candidate > best_level_candidate && root_id_candidate != nullptr) { best_root_id_candidate = root_id_candidate; best_level_candidate = level_candidate; } @@ -1285,7 +1357,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int bmain, id_owner, curr_level + 1, &best_level_placeholder); } - BLI_assert(best_root_id_candidate != NULL); + BLI_assert(best_root_id_candidate != nullptr); BLI_assert((best_root_id_candidate->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) == 0); *r_best_level = best_level_candidate; @@ -1303,14 +1375,14 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID /* Hierarchy root already set, and not matching currently proposed one, try to find which is * best. */ - if (id->override_library->hierarchy_root != NULL) { + if (id->override_library->hierarchy_root != nullptr) { /* Check if given `id_from` matches with the hierarchy of the linked reference ID, in which * case we assume that the given hierarchy root is the 'real' one. * * NOTE: This can fail if user mixed dependencies between several overrides of a same * reference linked hierarchy. Not much to be done in that case, it's virtually impossible to * fix this automatically in a reliable way. */ - if (id_from == NULL || !ID_IS_OVERRIDE_LIBRARY_REAL(id_from)) { + if (id_from == nullptr || !ID_IS_OVERRIDE_LIBRARY_REAL(id_from)) { /* Too complicated to deal with for now. */ CLOG_WARN(&LOG, "Inconsistency in library override hierarchy of ID '%s'.\n" @@ -1323,12 +1395,12 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID } ID *id_from_ref = id_from->override_library->reference; - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, - id->override_library->reference); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(BLI_ghash_lookup( + bmain->relations->relations_from_pointers, id->override_library->reference)); + BLI_assert(entry != nullptr); bool do_replace_root = false; - for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != NULL; + for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr; from_id_entry = from_id_entry->next) { if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships as actual dependencies. */ @@ -1365,10 +1437,11 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID id->override_library->hierarchy_root = id_root; } - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); - for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships as actual dependencies. */ @@ -1376,7 +1449,7 @@ static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID } ID *to_id = *to_id_entry->id_pointer.to; - if (ELEM(to_id, NULL, id)) { + if (ELEM(to_id, nullptr, id)) { continue; } if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id->lib)) { @@ -1398,18 +1471,18 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { continue; } - if (id->override_library->hierarchy_root != NULL) { + if (id->override_library->hierarchy_root != nullptr) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id->override_library->hierarchy_root) || id->override_library->hierarchy_root->lib != id->lib) { CLOG_ERROR( &LOG, "Existing override hierarchy root ('%s') for ID '%s' is invalid, will try to find a " "new valid one", - id->override_library->hierarchy_root != NULL ? + id->override_library->hierarchy_root != nullptr ? id->override_library->hierarchy_root->name : "<NONE>", id->name); - id->override_library->hierarchy_root = NULL; + id->override_library->hierarchy_root = nullptr; } else { continue; @@ -1421,7 +1494,7 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) int best_level = 0; ID *id_root = lib_override_root_find(bmain, id, best_level, &best_level); - if (!ELEM(id_root->override_library->hierarchy_root, id_root, NULL)) { + if (!ELEM(id_root->override_library->hierarchy_root, id_root, nullptr)) { CLOG_WARN(&LOG, "Potential inconsistency in library override hierarchy of ID '%s', detected as " "part of the hierarchy of '%s', which has a different root '%s'", @@ -1431,9 +1504,9 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } - lib_override_root_hierarchy_set(bmain, id_root, id, NULL); + lib_override_root_hierarchy_set(bmain, id_root, id, nullptr); - BLI_assert(id->override_library->hierarchy_root != NULL); + BLI_assert(id->override_library->hierarchy_root != nullptr); } FOREACH_MAIN_ID_END; @@ -1445,14 +1518,14 @@ static void lib_override_library_remap(Main *bmain, GHash *linkedref_to_old_override) { ID *id; - struct IDRemapper *remapper = BKE_id_remapper_create(); - LinkNode *nomain_ids = NULL; + IDRemapper *remapper = BKE_id_remapper_create(); + LinkNode *nomain_ids = nullptr; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); - if (id_override_old == NULL) { + ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id)); + if (id_override_old == nullptr) { continue; } @@ -1464,7 +1537,8 @@ static void lib_override_library_remap(Main *bmain, /* Remap no-main override IDs we just created too. */ GHashIterator linkedref_to_old_override_iter; GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) { - ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter); + ID *id_override_old_iter = static_cast<ID *>( + BLI_ghashIterator_getValue(&linkedref_to_old_override_iter)); if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) { continue; } @@ -1480,7 +1554,7 @@ static void lib_override_library_remap(Main *bmain, remapper, ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); BKE_id_remapper_free(remapper); - BLI_linklist_free(nomain_ids, NULL); + BLI_linklist_free(nomain_ids, nullptr); } static bool lib_override_library_resync(Main *bmain, @@ -1500,7 +1574,7 @@ static bool lib_override_library_resync(Main *bmain, ID *id; if (id_root_reference->tag & LIB_TAG_MISSING) { - BKE_reportf(reports != NULL ? reports->reports : NULL, + BKE_reportf(reports != nullptr ? reports->reports : nullptr, RPT_ERROR, "Impossible to resync data-block %s and its dependencies, as its linked reference " "is missing", @@ -1509,14 +1583,15 @@ static bool lib_override_library_resync(Main *bmain, } BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = {.bmain = bmain, - .scene = scene, - .id_root = id_root, - .hierarchy_root_id = id_root->override_library->hierarchy_root, - .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING, - .is_override = true, - .is_resync = true}; + LibOverrideGroupTagData data{}; + data.bmain = bmain; + data.scene = scene; + data.id_root = id_root; + data.hierarchy_root_id = id_root->override_library->hierarchy_root; + data.tag = LIB_TAG_DOIT; + data.missing_tag = LIB_TAG_MISSING; + data.is_override = true; + data.is_resync = true; lib_override_group_tag_data_object_to_collection_init(&data); /* Mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, populated from @@ -1526,9 +1601,9 @@ static bool lib_override_library_resync(Main *bmain, /* Only tag linked IDs from related linked reference hierarchy that are actually part of * the sub-trees of each detected sub-roots needing resync. */ - for (LinkNode *resync_root_link = id_resync_roots; resync_root_link != NULL; + for (LinkNode *resync_root_link = id_resync_roots; resync_root_link != nullptr; resync_root_link = resync_root_link->next) { - ID *id_resync_root = resync_root_link->link; + ID *id_resync_root = static_cast<ID *>(resync_root_link->link); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_resync_root)); if ((id_resync_root->tag & LIB_TAG_NO_MAIN) != 0) { @@ -1550,12 +1625,12 @@ static bool lib_override_library_resync(Main *bmain, if (id_resync_root_reference->tag & LIB_TAG_MISSING) { BKE_reportf( - reports != NULL ? reports->reports : NULL, + reports != nullptr ? reports->reports : nullptr, RPT_ERROR, "Impossible to resync data-block %s and its dependencies, as its linked reference " "is missing", id_root->name + 2); - BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr); BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); return false; @@ -1594,7 +1669,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, NULL); + IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, nullptr); ID *reference_id = id_override_library->reference; if (GS(reference_id->name) != GS(id->name)) { switch (GS(id->name)) { @@ -1666,19 +1741,25 @@ 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, NULL, true); + bmain, + nullptr, + id_root_reference, + id_root->override_library->hierarchy_root, + nullptr, + true, + false); if (!success) { - BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr); return success; } ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id)); BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0); @@ -1686,14 +1767,14 @@ static bool lib_override_library_resync(Main *bmain, * duplicated from the reference ID with 'no main' option, it should currently be the same * as the reference ID one). */ BLI_assert(/*!ID_IS_LINKED(id_override_new) || */ id_override_new->lib == id->lib); - BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib); + BLI_assert(id_override_old == nullptr || id_override_old->lib == id_root->lib); id_override_new->lib = id_root->lib; /* Remap step below will tag directly linked ones properly as needed. */ if (ID_IS_LINKED(id_override_new)) { id_override_new->tag |= LIB_TAG_INDIRECT; } - if (id_override_old != NULL) { + if (id_override_old != nullptr) { /* Swap the names between old override ID and new one. */ char id_name_buf[MAX_ID_NAME]; memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf)); @@ -1704,6 +1785,8 @@ static bool lib_override_library_resync(Main *bmain, id_override_old->tag |= LIB_TAG_NO_MAIN; id_override_new->tag &= ~LIB_TAG_NO_MAIN; + lib_override_object_posemode_transfer(id_override_new, id_override_old); + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)); @@ -1712,10 +1795,10 @@ static bool lib_override_library_resync(Main *bmain, /* Copy over overrides rules from old override ID to new one. */ BLI_duplicatelist(&id_override_new->override_library->properties, &id_override_old->override_library->properties); - IDOverrideLibraryProperty *op_new = - id_override_new->override_library->properties.first; - IDOverrideLibraryProperty *op_old = - id_override_old->override_library->properties.first; + IDOverrideLibraryProperty *op_new = static_cast<IDOverrideLibraryProperty *>( + id_override_new->override_library->properties.first); + IDOverrideLibraryProperty *op_old = static_cast<IDOverrideLibraryProperty *>( + id_override_old->override_library->properties.first); for (; op_new; op_new = op_new->next, op_old = op_old->next) { lib_override_library_property_copy(op_new, op_old); } @@ -1740,20 +1823,20 @@ static bool lib_override_library_resync(Main *bmain, BKE_main_collection_sync(bmain); - LinkNode *id_override_old_list = NULL; + LinkNode *id_override_old_list = nullptr; /* We need to apply override rules in a separate loop, after all ID pointers have been properly * remapped, and all new local override IDs have gotten their proper original names, otherwise * override operations based on those ID names would fail. */ FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) { + if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) { ID *id_override_new = id->newid; if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) { continue; } - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id)); - if (id_override_old == NULL) { + if (id_override_old == nullptr) { continue; } if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) { @@ -1784,7 +1867,7 @@ static bool lib_override_library_resync(Main *bmain, RNA_struct_override_apply(bmain, &rnaptr_dst, &rnaptr_src, - NULL, + nullptr, id_override_new->override_library, do_hierarchy_enforce ? RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS : @@ -1799,18 +1882,18 @@ static bool lib_override_library_resync(Main *bmain, /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages * of the old one. * This is necessary in case said old ID is not in Main anymore. */ - struct IDRemapper *id_remapper = BKE_id_remapper_create(); + IDRemapper *id_remapper = BKE_id_remapper_create(); BKE_libblock_relink_multiple(bmain, id_override_old_list, ID_REMAP_TYPE_CLEANUP, id_remapper, ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); - for (LinkNode *ln_iter = id_override_old_list; ln_iter != NULL; ln_iter = ln_iter->next) { - ID *id_override_old = ln_iter->link; + for (LinkNode *ln_iter = id_override_old_list; ln_iter != nullptr; ln_iter = ln_iter->next) { + ID *id_override_old = static_cast<ID *>(ln_iter->link); id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT; } BKE_id_remapper_free(id_remapper); - BLI_linklist_free(id_override_old_list, NULL); + BLI_linklist_free(id_override_old_list, nullptr); /* Delete old override IDs. * Note that we have to use tagged group deletion here, since ID deletion also uses @@ -1821,10 +1904,10 @@ static bool lib_override_library_resync(Main *bmain, /* Note that this works because linked IDs are always after local ones (including * overrides), so we will only ever tag an old override ID after we have already checked it * in this loop, hence we cannot untag it later. */ - if (id->newid != NULL && id->lib == id_root_reference->lib) { - ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + if (id->newid != nullptr && id->lib == id_root_reference->lib) { + ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id)); - if (id_override_old != NULL) { + if (id_override_old != nullptr) { id->newid->tag &= ~LIB_TAG_DOIT; id_override_old->tag |= LIB_TAG_DOIT; if (id_override_old->tag & LIB_TAG_NO_MAIN) { @@ -1868,19 +1951,19 @@ static bool lib_override_library_resync(Main *bmain, FOREACH_MAIN_ID_END; /* Cleanup, many pointers in this GHash are already invalid now. */ - BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr); BKE_id_multi_tagged_delete(bmain); /* At this point, `id_root` may have been resynced, therefore deleted. In that case we need to * update it to its new version. */ - if (id_root_reference->newid != NULL) { + if (id_root_reference->newid != nullptr) { id_root = id_root_reference->newid; } if (user_edited_overrides_deletion_count > 0) { - BKE_reportf(reports != NULL ? reports->reports : NULL, + BKE_reportf(reports != nullptr ? reports->reports : nullptr, RPT_WARNING, "During resync of data-block %s, %d obsolete overrides were deleted, that had " "local changes defined by user", @@ -1897,7 +1980,7 @@ static bool lib_override_library_resync(Main *bmain, lib_override_library_create_post_process(bmain, scene, view_layer, - NULL, + nullptr, id_root_reference, id_root, override_resync_residual_storage, @@ -1919,8 +2002,10 @@ bool BKE_lib_override_library_resync(Main *bmain, const bool do_hierarchy_enforce, BlendFileReadReport *reports) { - ListBase no_main_ids_list = {NULL}; - LinkNode id_resync_roots = {.link = id_root, .next = NULL}; + ListBase no_main_ids_list = {nullptr}; + LinkNode id_resync_roots{}; + id_resync_roots.link = id_root; + id_resync_roots.next = nullptr; const bool success = lib_override_library_resync(bmain, scene, @@ -1954,14 +2039,14 @@ static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id) { if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); - if (id_type->owner_get != NULL) { + if (id_type->owner_get != nullptr) { id = id_type->owner_get(bmain, id); } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); } ID *hierarchy_root_id = id->override_library->hierarchy_root; - BLI_assert(hierarchy_root_id != NULL); + BLI_assert(hierarchy_root_id != nullptr); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(hierarchy_root_id)); return hierarchy_root_id; } @@ -1996,8 +2081,9 @@ static bool lib_override_resync_tagging_finalize_recurse( return false; } - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { /* This ID has already been processed. */ @@ -2031,7 +2117,7 @@ static bool lib_override_resync_tagging_finalize_recurse( id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; bool is_ancestor_tagged_for_resync = false; - for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL; + for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != nullptr; entry_item = entry_item->next) { if (entry_item->usage_flag & (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) { @@ -2088,13 +2174,11 @@ static bool lib_override_resync_tagging_finalize_recurse( CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name); - if (id_root->override_library == NULL) { - BLI_assert(0); - } + BLI_assert(id_root->override_library != nullptr); LinkNodePair **id_resync_roots_p; if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { - *id_resync_roots_p = MEM_callocN(sizeof(**id_resync_roots_p), __func__); + *id_resync_roots_p = MEM_cnew<LinkNodePair>(__func__); } BLI_linklist_append(*id_resync_roots_p, id); @@ -2129,13 +2213,14 @@ static void lib_override_library_main_resync_on_library_indirect_level( /* Detect all linked data that would need to be overridden if we had to create an override from * those used by current existing overrides. */ - LibOverrideGroupTagData data = {.bmain = bmain, - .scene = scene, - .id_root = NULL, - .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING, - .is_override = false, - .is_resync = true}; + LibOverrideGroupTagData data = {}; + data.bmain = bmain; + data.scene = scene; + data.id_root = nullptr; + data.tag = LIB_TAG_DOIT; + data.missing_tag = LIB_TAG_MISSING; + data.is_override = false; + data.is_resync = true; lib_override_group_tag_data_object_to_collection_init(&data); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { @@ -2188,10 +2273,11 @@ static void lib_override_library_main_resync_on_library_indirect_level( continue; } - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); - BLI_assert(entry != NULL); + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(bmain->relations->relations_from_pointers, id)); + BLI_assert(entry != nullptr); - for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL; + for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != nullptr; entry_item = entry_item->next) { if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) { continue; @@ -2224,13 +2310,14 @@ static void lib_override_library_main_resync_on_library_indirect_level( BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots); while (!BLI_ghashIterator_done(id_roots_iter)) { - ID *id_root = BLI_ghashIterator_getKey(id_roots_iter); - LinkNodePair *id_resync_roots = BLI_ghashIterator_getValue(id_roots_iter); + ID *id_root = static_cast<ID *>(BLI_ghashIterator_getKey(id_roots_iter)); + LinkNodePair *id_resync_roots = static_cast<LinkNodePair *>( + BLI_ghashIterator_getValue(id_roots_iter)); CLOG_INFO( &LOG, 2, "Checking validity of computed TODO data for root '%s'... \n", id_root->name); - for (LinkNode *id_resync_root_iter = id_resync_roots->list; id_resync_root_iter != NULL; + for (LinkNode *id_resync_root_iter = id_resync_roots->list; id_resync_root_iter != nullptr; id_resync_root_iter = id_resync_root_iter->next) { - ID *id_resync_root = id_resync_root_iter->link; + ID *id_resync_root = static_cast<ID *>(id_resync_root_iter->link); BLI_assert(id_resync_root == id_root || !BLI_ghash_haskey(id_roots, id_resync_root)); if (id_resync_root == id_root) { BLI_assert(id_resync_root_iter == id_resync_roots->list && @@ -2248,13 +2335,14 @@ static void lib_override_library_main_resync_on_library_indirect_level( BKE_main_relations_free(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - ListBase no_main_ids_list = {NULL}; + ListBase no_main_ids_list = {nullptr}; GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots); while (!BLI_ghashIterator_done(id_roots_iter)) { - ID *id_root = BLI_ghashIterator_getKey(id_roots_iter); + ID *id_root = static_cast<ID *>(BLI_ghashIterator_getKey(id_roots_iter)); Library *library = id_root->lib; - LinkNodePair *id_resync_roots = BLI_ghashIterator_getValue(id_roots_iter); + LinkNodePair *id_resync_roots = static_cast<LinkNodePair *>( + BLI_ghashIterator_getValue(id_roots_iter)); if (ID_IS_LINKED(id_root)) { id_root->lib->tag |= LIBRARY_TAG_RESYNC_REQUIRED; @@ -2286,7 +2374,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( } } - BLI_linklist_free(id_resync_roots->list, NULL); + BLI_linklist_free(id_resync_roots->list, nullptr); BLI_ghashIterator_step(id_roots_iter); } BLI_ghashIterator_free(id_roots_iter); @@ -2314,10 +2402,10 @@ static void lib_override_library_main_resync_on_library_indirect_level( } FOREACH_MAIN_ID_END; - BLI_ghash_free(id_roots, NULL, MEM_freeN); + BLI_ghash_free(id_roots, nullptr, 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. */ + * freed, and therefore set to nullptr. Attempt to fix this as best as possible. */ BKE_lib_override_library_main_hierarchy_root_ensure(bmain); if (do_reports_recursive_resync_timing) { @@ -2332,7 +2420,7 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) } ID *id_owner = cb_data->id_owner; ID *id = *cb_data->id_pointer; - if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) { + if (id != nullptr && ID_IS_LINKED(id) && id->lib != id_owner->lib) { const int owner_library_indirect_level = ID_IS_LINKED(id_owner) ? id_owner->lib->temp_index : 0; if (owner_library_indirect_level > 200) { @@ -2344,7 +2432,6 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) id_owner->lib->filepath, id->name, id->lib->filepath); - BLI_assert(0); return IDWALK_RET_NOP; } @@ -2394,12 +2481,13 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* We use a specific collection to gather/store all 'orphaned' override collections and objects * generated by re-sync-process. This avoids putting them in scene's master collection. */ #define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS" - Collection *override_resync_residual_storage = BLI_findstring( - &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2); - if (override_resync_residual_storage != NULL && ID_IS_LINKED(override_resync_residual_storage)) { - override_resync_residual_storage = NULL; + Collection *override_resync_residual_storage = static_cast<Collection *>(BLI_findstring( + &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2)); + if (override_resync_residual_storage != nullptr && + ID_IS_LINKED(override_resync_residual_storage)) { + override_resync_residual_storage = nullptr; } - if (override_resync_residual_storage == NULL) { + if (override_resync_residual_storage == nullptr) { override_resync_residual_storage = BKE_collection_add( bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); /* Hide the collection from viewport and render. */ @@ -2427,7 +2515,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( - bmain, scene, view_layer, NULL, NULL, NULL, override_resync_residual_storage, true); + bmain, scene, view_layer, nullptr, nullptr, nullptr, override_resync_residual_storage, true); if (BKE_collection_is_empty(override_resync_residual_storage)) { BKE_collection_delete(bmain, override_resync_residual_storage, true); @@ -2450,14 +2538,15 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = {.bmain = bmain, - .scene = NULL, - .id_root = id_root, - .hierarchy_root_id = id_root->override_library->hierarchy_root, - .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING, - .is_override = true, - .is_resync = false}; + LibOverrideGroupTagData data{}; + data.bmain = bmain; + data.scene = nullptr; + data.id_root = id_root; + data.hierarchy_root_id = id_root->override_library->hierarchy_root; + data.tag = LIB_TAG_DOIT; + data.missing_tag = LIB_TAG_MISSING; + data.is_override = true; + data.is_resync = false; lib_override_group_tag_data_object_to_collection_init(&data); lib_override_overrides_group_tag(&data); @@ -2499,19 +2588,19 @@ void BKE_lib_override_library_make_local(ID *id) BKE_lib_override_library_free(&id->override_library, true); Key *shape_key = BKE_key_from_id(id); - if (shape_key != NULL) { + if (shape_key != nullptr) { shape_key->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; } if (GS(id->name) == ID_SCE) { Collection *master_collection = ((Scene *)id)->master_collection; - if (master_collection != NULL) { + if (master_collection != nullptr) { master_collection->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; } } bNodeTree *node_tree = ntreeFromID(id); - if (node_tree != NULL) { + if (node_tree != nullptr) { node_tree->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; } } @@ -2519,8 +2608,8 @@ void BKE_lib_override_library_make_local(ID *id) BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure( IDOverrideLibrary *override) { - if (override->runtime == NULL) { - override->runtime = MEM_callocN(sizeof(*override->runtime), __func__); + if (override->runtime == nullptr) { + override->runtime = MEM_cnew<IDOverrideLibraryRuntime>(__func__); } return override->runtime; } @@ -2529,10 +2618,13 @@ BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure( BLI_INLINE GHash *override_library_rna_path_mapping_ensure(IDOverrideLibrary *override) { IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_runtime_ensure(override); - if (override_runtime->rna_path_to_override_properties == NULL) { + if (override_runtime->rna_path_to_override_properties == nullptr) { override_runtime->rna_path_to_override_properties = BLI_ghash_new( BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__); - for (IDOverrideLibraryProperty *op = override->properties.first; op != NULL; op = op->next) { + for (IDOverrideLibraryProperty *op = + static_cast<IDOverrideLibraryProperty *>(override->properties.first); + op != nullptr; + op = op->next) { BLI_ghash_insert(override_runtime->rna_path_to_override_properties, op->rna_path, op); } } @@ -2544,7 +2636,7 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_find(IDOverrideLibr const char *rna_path) { GHash *override_runtime = override_library_rna_path_mapping_ensure(override); - return BLI_ghash_lookup(override_runtime, rna_path); + return static_cast<IDOverrideLibraryProperty *>(BLI_ghash_lookup(override_runtime, rna_path)); } IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibrary *override, @@ -2553,8 +2645,8 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibra { IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path); - if (op == NULL) { - op = MEM_callocN(sizeof(IDOverrideLibraryProperty), __func__); + if (op == nullptr) { + op = MEM_cnew<IDOverrideLibraryProperty>(__func__); op->rna_path = BLI_strdup(rna_path); BLI_addtail(&override->properties, op); @@ -2588,8 +2680,9 @@ void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst, op_dst->rna_path = BLI_strdup(op_src->rna_path); BLI_duplicatelist(&op_dst->operations, &op_src->operations); - for (IDOverrideLibraryPropertyOperation *opop_dst = op_dst->operations.first, - *opop_src = op_src->operations.first; + for (IDOverrideLibraryPropertyOperation * + opop_dst = static_cast<IDOverrideLibraryPropertyOperation *>(op_dst->operations.first), + *opop_src = static_cast<IDOverrideLibraryPropertyOperation *>(op_src->operations.first); opop_dst; opop_dst = opop_dst->next, opop_src = opop_src->next) { lib_override_library_property_operation_copy(opop_dst, opop_src); @@ -2598,7 +2691,7 @@ void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst, void lib_override_library_property_clear(IDOverrideLibraryProperty *op) { - BLI_assert(op->rna_path != NULL); + BLI_assert(op->rna_path != nullptr); MEM_freeN(op->rna_path); @@ -2611,11 +2704,11 @@ void lib_override_library_property_clear(IDOverrideLibraryProperty *op) void BKE_lib_override_library_property_delete(IDOverrideLibrary *override, IDOverrideLibraryProperty *override_property) { - if (!ELEM(NULL, override->runtime, override->runtime->rna_path_to_override_properties)) { + if (!ELEM(nullptr, override->runtime, override->runtime->rna_path_to_override_properties)) { BLI_ghash_remove(override->runtime->rna_path_to_override_properties, override_property->rna_path, - NULL, - NULL); + nullptr, + nullptr); } lib_override_library_property_clear(override_property); BLI_freelinkN(&override->properties, override_property); @@ -2637,74 +2730,75 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ *r_strict = true; } - if (subitem_locname != NULL) { - opop = BLI_findstring_ptr(&override_property->operations, - subitem_locname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name)); + if (subitem_locname != nullptr) { + opop = static_cast<IDOverrideLibraryPropertyOperation *>( + BLI_findstring_ptr(&override_property->operations, + subitem_locname, + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name))); - if (opop == NULL) { - return NULL; + if (opop == nullptr) { + return nullptr; } - if (subitem_refname == NULL || opop->subitem_reference_name == NULL) { - return subitem_refname == opop->subitem_reference_name ? opop : NULL; + if (subitem_refname == nullptr || opop->subitem_reference_name == nullptr) { + return subitem_refname == opop->subitem_reference_name ? opop : nullptr; } - return (subitem_refname != NULL && opop->subitem_reference_name != NULL && + return (subitem_refname != nullptr && opop->subitem_reference_name != nullptr && STREQ(subitem_refname, opop->subitem_reference_name)) ? opop : - NULL; + nullptr; } - if (subitem_refname != NULL) { - opop = BLI_findstring_ptr( - &override_property->operations, - subitem_refname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name)); + if (subitem_refname != nullptr) { + opop = static_cast<IDOverrideLibraryPropertyOperation *>( + BLI_findstring_ptr(&override_property->operations, + subitem_refname, + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name))); - if (opop == NULL) { - return NULL; + if (opop == nullptr) { + return nullptr; } - if (subitem_locname == NULL || opop->subitem_local_name == NULL) { - return subitem_locname == opop->subitem_local_name ? opop : NULL; + if (subitem_locname == nullptr || opop->subitem_local_name == nullptr) { + return subitem_locname == opop->subitem_local_name ? opop : nullptr; } - return (subitem_locname != NULL && opop->subitem_local_name != NULL && + return (subitem_locname != nullptr && opop->subitem_local_name != nullptr && STREQ(subitem_locname, opop->subitem_local_name)) ? opop : - NULL; + nullptr; } - if ((opop = BLI_listbase_bytes_find( + if ((opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find( &override_property->operations, &subitem_locindex, sizeof(subitem_locindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { - return ELEM(subitem_refindex, -1, opop->subitem_reference_index) ? opop : NULL; + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index))))) { + return ELEM(subitem_refindex, -1, opop->subitem_reference_index) ? opop : nullptr; } - if ((opop = BLI_listbase_bytes_find( + if ((opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find( &override_property->operations, &subitem_refindex, sizeof(subitem_refindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_index)))) { - return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : NULL; + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_index))))) { + return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : nullptr; } /* `index == -1` means all indices, that is a valid fallback in case we requested specific index. */ if (!strict && (subitem_locindex != subitem_defindex) && - (opop = BLI_listbase_bytes_find( + (opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find( &override_property->operations, &subitem_defindex, sizeof(subitem_defindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index))))) { if (r_strict) { *r_strict = false; } return opop; } - return NULL; + return nullptr; } IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_get( @@ -2727,8 +2821,8 @@ IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_ strict, r_strict); - if (opop == NULL) { - opop = MEM_callocN(sizeof(IDOverrideLibraryPropertyOperation), __func__); + if (opop == nullptr) { + opop = MEM_cnew<IDOverrideLibraryPropertyOperation>(__func__); opop->operation = operation; if (subitem_locname) { opop->subitem_local_name = BLI_strdup(subitem_locname); @@ -2782,13 +2876,13 @@ void BKE_lib_override_library_property_operation_delete( } bool BKE_lib_override_library_property_operation_operands_validate( - struct IDOverrideLibraryPropertyOperation *override_property_operation, - struct PointerRNA *ptr_dst, - struct PointerRNA *ptr_src, - struct PointerRNA *ptr_storage, - struct PropertyRNA *prop_dst, - struct PropertyRNA *prop_src, - struct PropertyRNA *prop_storage) + IDOverrideLibraryPropertyOperation *override_property_operation, + PointerRNA *ptr_dst, + PointerRNA *ptr_src, + PointerRNA *ptr_storage, + PropertyRNA *prop_dst, + PropertyRNA *prop_src, + PropertyRNA *prop_storage) { switch (override_property_operation->operation) { case IDOVERRIDE_LIBRARY_OP_NOOP: @@ -2798,7 +2892,7 @@ bool BKE_lib_override_library_property_operation_operands_validate( case IDOVERRIDE_LIBRARY_OP_SUBTRACT: ATTR_FALLTHROUGH; case IDOVERRIDE_LIBRARY_OP_MULTIPLY: - if (ptr_storage == NULL || ptr_storage->data == NULL || prop_storage == NULL) { + if (ptr_storage == nullptr || ptr_storage->data == nullptr || prop_storage == nullptr) { BLI_assert_msg(0, "Missing data to apply differential override operation."); return false; } @@ -2808,8 +2902,8 @@ bool BKE_lib_override_library_property_operation_operands_validate( case IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE: ATTR_FALLTHROUGH; case IDOVERRIDE_LIBRARY_OP_REPLACE: - if ((ptr_dst == NULL || ptr_dst->data == NULL || prop_dst == NULL) || - (ptr_src == NULL || ptr_src->data == NULL || prop_src == NULL)) { + if ((ptr_dst == nullptr || ptr_dst->data == nullptr || prop_dst == nullptr) || + (ptr_src == nullptr || ptr_src->data == nullptr || prop_src == nullptr)) { BLI_assert_msg(0, "Missing data to apply override operation."); return false; } @@ -2820,10 +2914,10 @@ bool BKE_lib_override_library_property_operation_operands_validate( void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports) { - if (id->override_library == NULL) { + if (id->override_library == nullptr) { return; } - if (id->override_library->reference == NULL) { + if (id->override_library->reference == nullptr) { /* This is a template ID, could be linked or local, not an override. */ return; } @@ -2834,7 +2928,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList * RPT_ERROR, "Data corruption: data-block '%s' is using itself as library override reference", id->name); - id->override_library->reference = NULL; + id->override_library->reference = nullptr; return; } if (!ID_IS_LINKED(id->override_library->reference)) { @@ -2846,7 +2940,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList * "library override reference", id->name, id->override_library->reference->name); - id->override_library->reference = NULL; + id->override_library->reference = nullptr; return; } } @@ -2856,7 +2950,7 @@ void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports) ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->override_library != NULL) { + if (id->override_library != nullptr) { BKE_lib_override_library_validate(bmain, id, reports); } } @@ -2869,7 +2963,7 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local) ID *reference = local->override_library->reference; - if (reference == NULL) { + if (reference == nullptr) { /* This is an override template, local status is always OK! */ return true; } @@ -2883,10 +2977,10 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local) Object *ob_local = (Object *)local; if (ob_local->type == OB_ARMATURE) { Object *ob_reference = (Object *)local->override_library->reference; - BLI_assert(ob_local->data != NULL); - BLI_assert(ob_reference->data != NULL); - BKE_pose_ensure(bmain, ob_local, ob_local->data, true); - BKE_pose_ensure(bmain, ob_reference, ob_reference->data, true); + BLI_assert(ob_local->data != nullptr); + BLI_assert(ob_reference->data != nullptr); + BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); + BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true); } } @@ -2896,15 +2990,16 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local) RNA_id_pointer_create(local, &rnaptr_local); RNA_id_pointer_create(reference, &rnaptr_reference); - if (!RNA_struct_override_matches(bmain, - &rnaptr_local, - &rnaptr_reference, - NULL, - 0, - local->override_library, - RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | - RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, - NULL)) { + if (!RNA_struct_override_matches( + bmain, + &rnaptr_local, + &rnaptr_reference, + nullptr, + 0, + local->override_library, + (eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | + RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN), + nullptr)) { local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; return false; } @@ -2918,7 +3013,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) ID *reference = local->override_library->reference; - if (reference == NULL) { + if (reference == nullptr) { /* This is an override template, reference is virtual, so its status is always OK! */ return true; } @@ -2942,10 +3037,10 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) Object *ob_local = (Object *)local; if (ob_local->type == OB_ARMATURE) { Object *ob_reference = (Object *)local->override_library->reference; - BLI_assert(ob_local->data != NULL); - BLI_assert(ob_reference->data != NULL); - BKE_pose_ensure(bmain, ob_local, ob_local->data, true); - BKE_pose_ensure(bmain, ob_reference, ob_reference->data, true); + BLI_assert(ob_local->data != nullptr); + BLI_assert(ob_reference->data != nullptr); + BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); + BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true); } } @@ -2956,11 +3051,11 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) if (!RNA_struct_override_matches(bmain, &rnaptr_local, &rnaptr_reference, - NULL, + nullptr, 0, local->override_library, RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, - NULL)) { + nullptr)) { local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; return false; } @@ -2971,8 +3066,8 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) { BLI_assert(!ID_IS_LINKED(local)); - BLI_assert(local->override_library != NULL); - const bool is_template = (local->override_library->reference == NULL); + BLI_assert(local->override_library != nullptr); + const bool is_template = (local->override_library->reference == nullptr); bool created = false; if (!is_template) { @@ -2990,10 +3085,10 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) Object *ob_local = (Object *)local; if (ob_local->type == OB_ARMATURE) { Object *ob_reference = (Object *)local->override_library->reference; - BLI_assert(ob_local->data != NULL); - BLI_assert(ob_reference->data != NULL); - BKE_pose_ensure(bmain, ob_local, ob_local->data, true); - BKE_pose_ensure(bmain, ob_reference, ob_reference->data, true); + BLI_assert(ob_local->data != nullptr); + BLI_assert(ob_reference->data != nullptr); + BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); + BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true); } } @@ -3001,15 +3096,16 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) RNA_id_pointer_create(local, &rnaptr_local); RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); - eRNAOverrideMatchResult report_flags = 0; - RNA_struct_override_matches(bmain, - &rnaptr_local, - &rnaptr_reference, - NULL, - 0, - local->override_library, - RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE, - &report_flags); + eRNAOverrideMatchResult report_flags = (eRNAOverrideMatchResult)0; + RNA_struct_override_matches( + bmain, + &rnaptr_local, + &rnaptr_reference, + nullptr, + 0, + local->override_library, + (eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE), + &report_flags); if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) { created = true; @@ -3035,8 +3131,9 @@ struct LibOverrideOpCreateData { static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, void *taskdata) { - struct LibOverrideOpCreateData *create_data = BLI_task_pool_user_data(pool); - ID *id = taskdata; + LibOverrideOpCreateData *create_data = static_cast<LibOverrideOpCreateData *>( + BLI_task_pool_user_data(pool)); + ID *id = static_cast<ID *>(taskdata); if (BKE_lib_override_library_operations_create(create_data->bmain, id)) { /* Technically no need for atomic, all jobs write the same value and we only care if one did @@ -3065,12 +3162,14 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for * #BKE_lib_override_library_operations_create is not a problem then. */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { if (ob->type == OB_ARMATURE) { - BLI_assert(ob->data != NULL); - BKE_pose_ensure(bmain, ob, ob->data, true); + BLI_assert(ob->data != nullptr); + BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true); } } - struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false}; + LibOverrideOpCreateData create_pool_data{}; + create_pool_data.bmain = bmain; + create_pool_data.changed = false; TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH); FOREACH_MAIN_ID_BEGIN (bmain, id) { @@ -3081,14 +3180,15 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for if (GS(id->name) == ID_OB) { Object *ob = (Object *)id; if (ob->type == OB_ARMATURE) { - BLI_assert(ob->data != NULL); - BKE_pose_ensure(bmain, ob, ob->data, true); + BLI_assert(ob->data != nullptr); + BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true); } } /* Only check overrides if we do have the real reference data available, and not some empty * 'placeholder' for missing data (broken links). */ if ((id->override_library->reference->tag & LIB_TAG_MISSING) == 0) { - BLI_task_pool_push(task_pool, lib_override_library_operations_create_cb, id, false, NULL); + BLI_task_pool_push( + task_pool, lib_override_library_operations_create_cb, id, false, nullptr); } else { BKE_lib_override_library_properties_tag( @@ -3159,10 +3259,10 @@ static bool lib_override_library_id_reset_do(Main *bmain, ptr = RNA_property_pointer_get(&ptr, prop); ptr_lib = RNA_property_pointer_get(&ptr_lib, prop_lib); } - if (ptr.owner_id != NULL && ptr_lib.owner_id != NULL) { + if (ptr.owner_id != nullptr && ptr_lib.owner_id != nullptr) { BLI_assert(ptr.type == ptr_lib.type); do_op_delete = !(RNA_struct_is_ID(ptr.type) && - ptr.owner_id->override_library != NULL && + ptr.owner_id->override_library != nullptr && ptr.owner_id->override_library->reference == ptr_lib.owner_id); } } @@ -3194,7 +3294,7 @@ void BKE_lib_override_library_id_reset(Main *bmain, } if (lib_override_library_id_reset_do(bmain, id_root, do_reset_system_override)) { - if (id_root->override_library->runtime != NULL && + if (id_root->override_library->runtime != nullptr && (id_root->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) != 0) { BKE_lib_override_library_update(bmain, id_root); @@ -3212,13 +3312,13 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, } void **entry_vp = BLI_ghash_lookup_p(bmain->relations->relations_from_pointers, id_root); - if (entry_vp == NULL) { + if (entry_vp == nullptr) { /* This ID is not used by nor using any other ID. */ lib_override_library_id_reset_do(bmain, id_root, do_reset_system_override); return; } - MainIDRelationsEntry *entry = *entry_vp; + MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(*entry_vp); if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { /* This ID has already been processed. */ return; @@ -3230,7 +3330,7 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, * relationship hierarchy. */ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr; to_id_entry = to_id_entry->next) { if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) { /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as @@ -3238,9 +3338,9 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, continue; } /* We only consider IDs from the same library. */ - if (*to_id_entry->id_pointer.to != NULL) { + if (*to_id_entry->id_pointer.to != nullptr) { ID *to_id = *to_id_entry->id_pointer.to; - if (to_id->override_library != NULL) { + if (to_id->override_library != nullptr) { lib_override_library_id_hierarchy_recursive_reset(bmain, to_id, do_reset_system_override); } } @@ -3259,7 +3359,7 @@ void BKE_lib_override_library_id_hierarchy_reset(Main *bmain, ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || id->override_library->runtime == NULL || + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || id->override_library->runtime == nullptr || (id->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) == 0) { continue; } @@ -3269,11 +3369,11 @@ void BKE_lib_override_library_id_hierarchy_reset(Main *bmain, FOREACH_MAIN_ID_END; } -void BKE_lib_override_library_operations_tag(struct IDOverrideLibraryProperty *override_property, +void BKE_lib_override_library_operations_tag(IDOverrideLibraryProperty *override_property, const short tag, const bool do_set) { - if (override_property != NULL) { + if (override_property != nullptr) { if (do_set) { override_property->tag |= tag; } @@ -3292,18 +3392,18 @@ void BKE_lib_override_library_operations_tag(struct IDOverrideLibraryProperty *o } } -void BKE_lib_override_library_properties_tag(struct IDOverrideLibrary *override, +void BKE_lib_override_library_properties_tag(IDOverrideLibrary *override, const short tag, const bool do_set) { - if (override != NULL) { + if (override != nullptr) { LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) { BKE_lib_override_library_operations_tag(op, tag, do_set); } } } -void BKE_lib_override_library_main_tag(struct Main *bmain, const short tag, const bool do_set) +void BKE_lib_override_library_main_tag(Main *bmain, const short tag, const bool do_set) { ID *id; @@ -3315,7 +3415,7 @@ void BKE_lib_override_library_main_tag(struct Main *bmain, const short tag, cons FOREACH_MAIN_ID_END; } -void BKE_lib_override_library_id_unused_cleanup(struct ID *local) +void BKE_lib_override_library_id_unused_cleanup(ID *local) { if (ID_IS_OVERRIDE_LIBRARY_REAL(local)) { LISTBASE_FOREACH_MUTABLE ( @@ -3334,7 +3434,7 @@ void BKE_lib_override_library_id_unused_cleanup(struct ID *local) } } -void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain) +void BKE_lib_override_library_main_unused_cleanup(Main *bmain) { ID *id; @@ -3390,10 +3490,10 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) ID *tmp_id = BKE_id_copy_ex(bmain, local->override_library->reference, - NULL, + nullptr, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE_LOCAL_DATA_FLAG); - if (tmp_id == NULL) { + if (tmp_id == nullptr) { return; } @@ -3409,12 +3509,12 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) * collections' parents are fully runtime and reconstructed later. */ Key *local_key = BKE_key_from_id(local); Key *tmp_key = BKE_key_from_id(tmp_id); - if (local_key != NULL && tmp_key != NULL) { + if (local_key != nullptr && tmp_key != nullptr) { tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); tmp_key->id.lib = local_key->id.lib; } - PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL; + PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = nullptr; RNA_id_pointer_create(local, &rnaptr_src); RNA_id_pointer_create(tmp_id, &rnaptr_dst); if (local->override_library->storage) { @@ -3429,11 +3529,13 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) local->override_library, RNA_OVERRIDE_APPLY_FLAG_NOP); + lib_override_object_posemode_transfer(tmp_id, local); + /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. * So when we'll free tmp_id, we'll actually free old, outdated data from local. */ lib_override_id_swap(bmain, local, tmp_id); - if (local_key != NULL && tmp_key != NULL) { + if (local_key != nullptr && tmp_key != nullptr) { /* This is some kind of hard-coded 'always enforced override'. */ lib_override_id_swap(bmain, &local_key->id, &tmp_key->id); tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); @@ -3453,7 +3555,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) /* Fun times again, thanks to bone pointers in pose data of objects. We keep same ID addresses, * but internal data has changed for sure, so we need to invalidate pose-bones caches. */ LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->pose != NULL && ob->data == local) { + if (ob->pose != nullptr && ob->data == local) { BLI_assert(ob->type == OB_ARMATURE); ob->pose->flag |= POSE_RECALC; /* We need to clear pose bone pointers immediately, some code may access those before pose @@ -3468,7 +3570,7 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) /* XXX For until we get fully shadow copies, we still need to ensure storage releases * its usage of any ID pointers it may have. */ BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true); - local->override_library->storage = NULL; + local->override_library->storage = nullptr; } local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK; @@ -3492,7 +3594,7 @@ void BKE_lib_override_library_main_update(Main *bmain) G_MAIN = bmain; FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->override_library != NULL) { + if (id->override_library != nullptr) { BKE_lib_override_library_update(bmain, id); } } @@ -3501,7 +3603,7 @@ void BKE_lib_override_library_main_update(Main *bmain) G_MAIN = orig_gmain; } -bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id) +bool BKE_lib_override_library_id_is_user_deletable(Main *bmain, ID *id) { /* The only strong known case currently are objects used by override collections. */ /* TODO: There are most likely other cases... This may need to be addressed in a better way at @@ -3550,11 +3652,11 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, /* This is actually purely local data with an override template, or one of those embedded IDs * (root node trees, master collections or shape-keys) that cannot have their own override. * Nothing to do here! */ - return NULL; + return nullptr; } BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local)); - BLI_assert(override_storage != NULL); + BLI_assert(override_storage != nullptr); UNUSED_VARS_NDEBUG(override_storage); /* Forcefully ensure we know about all needed override operations. */ @@ -3582,7 +3684,7 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, * actually a (performances) issue here, before doing it. */ storage_id = BKE_id_copy((Main *)override_storage, local); - if (storage_id != NULL) { + if (storage_id != nullptr) { PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage; RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); RNA_id_pointer_create(local, &rnaptr_final); @@ -3591,11 +3693,11 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, if (!RNA_struct_override_store( bmain, &rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_library)) { BKE_id_free_ex(override_storage, storage_id, LIB_ID_FREE_NO_UI_USER, true); - storage_id = NULL; + storage_id = nullptr; } } #else - storage_id = NULL; + storage_id = nullptr; #endif local->override_library->storage = storage_id; @@ -3613,7 +3715,7 @@ void BKE_lib_override_library_operations_store_end( /* Nothing else to do here really, we need to keep all temp override storage data-blocks in * memory until whole file is written anyway (otherwise we'd get mem pointers overlap). */ - local->override_library->storage = NULL; + local->override_library->storage = nullptr; } void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage) diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c index 7fa9ba5dca9..88f6fbb0ead 100644 --- a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -83,7 +83,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_root, id_instance_hint, NULL); + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL, false); } static void lib_override_library_proxy_convert_do(Main *bmain, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 971db852463..2600a40153c 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -75,6 +75,7 @@ static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), { ID *id = *id_ptr; BLI_assert(id != NULL); + if (is_indirect) { id->runtime.remap.skipped_indirect++; } @@ -82,8 +83,9 @@ static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner), id->runtime.remap.skipped_direct++; } else { - BLI_assert(0); + BLI_assert_unreachable(); } + if (cb_flag & IDWALK_CB_USER) { id->runtime.remap.skipped_refcounted++; } @@ -553,7 +555,6 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ new_id ? new_id->name : "<NULL>", new_id, old_id->us - skipped_refcounted); - BLI_assert(0); } const int skipped_direct = old_id->runtime.remap.skipped_direct; diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 03e03dacfbc..b9ed783fa8c 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -502,13 +502,13 @@ BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img) } if (img) { - const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y); + data = MEM_mallocN(data_size, __func__); IMB_rect_from_float(img); /* Just in case... */ data->width = img->x; data->height = img->y; - memcpy(data->rect, img->rect, sz - sizeof(*data)); + memcpy(data->rect, img->rect, data_size - sizeof(*data)); } if (bmain) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 002b496393f..e5b875cadf9 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1123,15 +1123,16 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap BLI_ghash_free(gh_mat_map, NULL, NULL); } -void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval) +void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, const ID *data_eval) { ID *data_orig = ob_orig->data; short *orig_totcol = BKE_id_material_len_p(data_orig); Material ***orig_mat = BKE_id_material_array_p(data_orig); - short *eval_totcol = BKE_id_material_len_p(data_eval); - Material ***eval_mat = BKE_id_material_array_p(data_eval); + /* Can cast away const, because the data is not changed. */ + const short *eval_totcol = BKE_id_material_len_p((ID *)data_eval); + Material ***eval_mat = BKE_id_material_array_p((ID *)data_eval); if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) { return; @@ -1244,7 +1245,6 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) /* this should never happen and used to crash */ if (ob->actcol <= 0) { CLOG_ERROR(&LOG, "invalid material index %d, report a bug!", ob->actcol); - BLI_assert(0); return false; } diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 628f59ae449..0becea62810 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -31,6 +31,7 @@ #include "BLI_string.h" #include "BLI_task.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -60,6 +61,7 @@ #include "BLO_read_write.h" using blender::float3; +using blender::Vector; static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -208,46 +210,40 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address Mesh *mesh = (Mesh *)id; const bool is_undo = BLO_write_is_undo(writer); - CustomDataLayer *vlayers = nullptr, vlayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *elayers = nullptr, elayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *flayers = nullptr, flayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *llayers = nullptr, llayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; + Vector<CustomDataLayer, 16> vert_layers; + Vector<CustomDataLayer, 16> edge_layers; + Vector<CustomDataLayer, 16> loop_layers; + Vector<CustomDataLayer, 16> poly_layers; /* cache only - don't write */ mesh->mface = nullptr; mesh->totface = 0; memset(&mesh->fdata, 0, sizeof(mesh->fdata)); mesh->runtime = blender::dna::shallow_zero_initialize(); - flayers = flayers_buff; /* Do not store actual geometry data in case this is a library override ID. */ if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { mesh->mvert = nullptr; mesh->totvert = 0; memset(&mesh->vdata, 0, sizeof(mesh->vdata)); - vlayers = vlayers_buff; mesh->medge = nullptr; mesh->totedge = 0; memset(&mesh->edata, 0, sizeof(mesh->edata)); - elayers = elayers_buff; mesh->mloop = nullptr; mesh->totloop = 0; memset(&mesh->ldata, 0, sizeof(mesh->ldata)); - llayers = llayers_buff; mesh->mpoly = nullptr; mesh->totpoly = 0; memset(&mesh->pdata, 0, sizeof(mesh->pdata)); - players = players_buff; } else { - CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); - CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + CustomData_blend_write_prepare(mesh->vdata, vert_layers); + CustomData_blend_write_prepare(mesh->edata, edge_layers); + CustomData_blend_write_prepare(mesh->ldata, loop_layers); + CustomData_blend_write_prepare(mesh->pdata, poly_layers); } BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); @@ -264,33 +260,15 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); CustomData_blend_write( - writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); + writer, &mesh->vdata, vert_layers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); CustomData_blend_write( - writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); + writer, &mesh->edata, edge_layers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); /* fdata is really a dummy - written so slots align */ + CustomData_blend_write(writer, &mesh->fdata, {}, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); CustomData_blend_write( - writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); + writer, &mesh->ldata, loop_layers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); CustomData_blend_write( - writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); - - /* Free temporary data */ - - /* Free custom-data layers, when not assigned a buffer value. */ -#define CD_LAYERS_FREE(id) \ - if (id && id != id##_buff) { \ - MEM_freeN(id); \ - } \ - ((void)0) - - CD_LAYERS_FREE(vlayers); - CD_LAYERS_FREE(elayers); - // CD_LAYER_FREE(flayers); /* Never allocated. */ - CD_LAYERS_FREE(llayers); - CD_LAYERS_FREE(players); - -#undef CD_LAYERS_FREE + writer, &mesh->pdata, poly_layers, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); } static void mesh_blend_read_data(BlendDataReader *reader, ID *id) @@ -1577,6 +1555,19 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth) } } +void BKE_mesh_auto_smooth_flag_set(Mesh *me, + const bool use_auto_smooth, + const float auto_smooth_angle) +{ + if (use_auto_smooth) { + me->flag |= ME_AUTOSMOOTH; + me->smoothresh = auto_smooth_angle; + } + else { + me->flag &= ~ME_AUTOSMOOTH; + } +} + int poly_find_loop_from_vert(const MPoly *poly, const MLoop *loopstart, uint vert) { for (int j = 0; j < poly->totloop; j++, loopstart++) { @@ -1809,7 +1800,7 @@ void BKE_mesh_mselect_validate(Mesh *me) break; } default: { - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 0d34c8ad6b1..cc4a995b1b1 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -12,6 +12,7 @@ #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" @@ -943,24 +944,9 @@ static void curve_to_mesh_eval_ensure(Object &object) BKE_object_runtime_free_data(&taper_object); } -/* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */ -static const Mesh *get_evaluated_mesh_from_object(const Object *object) -{ - const Mesh *mesh = BKE_object_get_evaluated_mesh(object); - if (mesh) { - return mesh; - } - GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; - if (geometry_set_eval) { - return geometry_set_eval->get_mesh_for_read(); - } - return nullptr; -} - static const Curves *get_evaluated_curves_from_object(const Object *object) { - GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; - if (geometry_set_eval) { + if (GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval) { return geometry_set_eval->get_curves_for_read(); } return nullptr; @@ -968,12 +954,10 @@ static const Curves *get_evaluated_curves_from_object(const Object *object) static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_object) { - const Mesh *mesh = get_evaluated_mesh_from_object(evaluated_object); - if (mesh) { + if (const Mesh *mesh = BKE_object_get_evaluated_mesh(evaluated_object)) { return BKE_mesh_copy_for_eval(mesh, false); } - const Curves *curves = get_evaluated_curves_from_object(evaluated_object); - if (curves) { + if (const Curves *curves = get_evaluated_curves_from_object(evaluated_object)) { return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry)); } return nullptr; @@ -1392,7 +1376,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act for (i = 0; i < tot; i++) { CustomDataLayer *layer = &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; - float(*cos)[3], (*kbcos)[3]; + float(*kbcos)[3]; for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->uid == layer->uid) { @@ -1409,7 +1393,8 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act MEM_freeN(kb->data); } - cos = (float(*)[3])CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i); + const float(*cos)[3] = (const float(*)[3])CustomData_get_layer_n( + &mesh_src->vdata, CD_SHAPEKEY, i); kb->totelem = mesh_src->totvert; kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index ec2660a0145..de0489d668f 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -18,8 +18,9 @@ #include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_edgehash.h" - +#include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_span.hh" #include "BLI_utildefines.h" #include "BKE_customdata.h" @@ -27,6 +28,10 @@ #include "BKE_mesh.h" #include "BKE_multires.h" +using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; + /* -------------------------------------------------------------------- */ /** \name Polygon Calculations * \{ */ @@ -644,7 +649,7 @@ static void bm_corners_to_loops_ex(ID *id, MFace *mf = mface + findex; for (int i = 0; i < numTex; i++) { - MTFace *texface = (MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); + const MTFace *texface = (const MTFace *)CustomData_get_n(fdata, CD_MTFACE, findex, i); MLoopUV *mloopuv = (MLoopUV *)CustomData_get_n(ldata, CD_MLOOPUV, loopstart, i); copy_v2_v2(mloopuv->uv, texface->uv[0]); @@ -662,7 +667,7 @@ static void bm_corners_to_loops_ex(ID *id, for (int i = 0; i < numCol; i++) { MLoopCol *mloopcol = (MLoopCol *)CustomData_get_n(ldata, CD_PROP_BYTE_COLOR, loopstart, i); - MCol *mcol = (MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); + const MCol *mcol = (const MCol *)CustomData_get_n(fdata, CD_MCOL, findex, i); MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]); mloopcol++; @@ -678,7 +683,7 @@ static void bm_corners_to_loops_ex(ID *id, if (CustomData_has_layer(fdata, CD_TESSLOOPNORMAL)) { float(*lnors)[3] = (float(*)[3])CustomData_get(ldata, loopstart, CD_NORMAL); - short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); + const short(*tlnors)[3] = (short(*)[3])CustomData_get(fdata, findex, CD_TESSLOOPNORMAL); const int max = mf->v4 ? 4 : 3; for (int i = 0; i < max; i++, lnors++, tlnors++) { @@ -688,8 +693,8 @@ static void bm_corners_to_loops_ex(ID *id, if (CustomData_has_layer(fdata, CD_MDISPS)) { MDisps *ld = (MDisps *)CustomData_get(ldata, loopstart, CD_MDISPS); - MDisps *fd = (MDisps *)CustomData_get(fdata, findex, CD_MDISPS); - float(*disps)[3] = fd->disps; + const MDisps *fd = (const MDisps *)CustomData_get(fdata, findex, CD_MDISPS); + const float(*disps)[3] = fd->disps; int tot = mf->v4 ? 4 : 3; int corners; @@ -1113,58 +1118,49 @@ void BKE_mesh_flush_select_from_polys(Mesh *me) me->mvert, me->totvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); } -void BKE_mesh_flush_select_from_verts_ex(const MVert *mvert, - const int UNUSED(totvert), - const MLoop *mloop, - MEdge *medge, - const int totedge, - MPoly *mpoly, - const int totpoly) +static void mesh_flush_select_from_verts(const Span<MVert> verts, + const Span<MLoop> loops, + MutableSpan<MEdge> edges, + MutableSpan<MPoly> polys) { - MEdge *med; - MPoly *mp; - - /* edges */ - int i = totedge; - for (med = medge; i--; med++) { - if ((med->flag & ME_HIDE) == 0) { - if ((mvert[med->v1].flag & SELECT) && (mvert[med->v2].flag & SELECT)) { - med->flag |= SELECT; + for (const int i : edges.index_range()) { + if ((edges[i].flag & ME_HIDE) == 0) { + MEdge &edge = edges[i]; + if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) { + edge.flag |= SELECT; } else { - med->flag &= ~SELECT; + edge.flag &= ~SELECT; } } } - /* polys */ - i = totpoly; - for (mp = mpoly; i--; mp++) { - if ((mp->flag & ME_HIDE) == 0) { - bool ok = true; - const MLoop *ml; - int j; - j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - if ((mvert[ml->v].flag & SELECT) == 0) { - ok = false; - break; - } - } - - if (ok) { - mp->flag |= ME_FACE_SEL; - } - else { - mp->flag &= (char)~ME_FACE_SEL; + for (const int i : polys.index_range()) { + if (polys[i].flag & ME_HIDE) { + continue; + } + MPoly &poly = polys[i]; + bool all_verts_selected = true; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + if (!(verts[loop.v].flag & SELECT)) { + all_verts_selected = false; } } + if (all_verts_selected) { + poly.flag |= ME_FACE_SEL; + } + else { + poly.flag &= (char)~ME_FACE_SEL; + } } } + void BKE_mesh_flush_select_from_verts(Mesh *me) { - BKE_mesh_flush_select_from_verts_ex( - me->mvert, me->totvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + mesh_flush_select_from_verts({me->mvert, me->totvert}, + {me->mloop, me->totloop}, + {me->medge, me->totedge}, + {me->mpoly, me->totpoly}); } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 02c9f61957d..c960a7f35f1 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -290,7 +290,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) &target->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, target->totvert); } - float *source_mask; + const float *source_mask; if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) { source_mask = (float *)CustomData_get_layer(&source->vdata, CD_PAINT_MASK); } diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index f2e46c3bd92..c0b2b33c47c 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -37,13 +37,13 @@ /* User data. */ typedef struct { - const MPoly *mpolys; /* faces */ - const MLoop *mloops; /* faces's vertices */ - const MVert *mverts; /* vertices */ - const MLoopUV *luvs; /* texture coordinates */ - float (*lnors)[3]; /* loops' normals */ - float (*tangents)[4]; /* output tangents */ - int num_polys; /* number of polygons */ + const MPoly *mpolys; /* faces */ + const MLoop *mloops; /* faces's vertices */ + const MVert *mverts; /* vertices */ + const MLoopUV *luvs; /* texture coordinates */ + const float (*lnors)[3]; /* loops' normals */ + float (*tangents)[4]; /* output tangents */ + int num_polys; /* number of polygons */ } BKEMeshToTangent; /* Mikktspace's API */ @@ -103,7 +103,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts, const int UNUSED(numVerts), const MLoop *mloops, float (*r_looptangent)[4], - float (*loopnors)[3], + const float (*loopnors)[3], const MLoopUV *loopuvs, const int UNUSED(numLoops), const MPoly *mpolys, @@ -155,8 +155,7 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, float (*r_looptangents)[4], ReportList *reports) { - MLoopUV *loopuvs; - float(*loopnors)[3]; + const MLoopUV *loopuvs; /* Check we have valid texture coordinates first! */ if (uvmap) { @@ -173,7 +172,7 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, return; } - loopnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + const float(*loopnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL); if (!loopnors) { BKE_report( reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting"); @@ -205,10 +204,10 @@ typedef struct { const float (*precomputedFaceNormals)[3]; const float (*precomputedLoopNormals)[3]; const MLoopTri *looptri; - MLoopUV *mloopuv; /* texture coordinates */ - const MPoly *mpoly; /* indices */ - const MLoop *mloop; /* indices */ - const MVert *mvert; /* vertex coordinates */ + const MLoopUV *mloopuv; /* texture coordinates */ + const MPoly *mpoly; /* indices */ + const MLoop *mloop; /* indices */ + const MVert *mvert; /* vertex coordinates */ const float (*vert_normals)[3]; const float (*orco)[3]; float (*tangent)[4]; /* destination */ diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index ea3cc043267..7cb656d2357 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -69,7 +69,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, for (i = 0; i < numUV; i++) { MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); - MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); + const MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; pidx++, lidx++, findex++, texface++) { @@ -81,7 +81,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, for (i = 0; i < numCol; i++) { MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); - MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_PROP_BYTE_COLOR, i); + const MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_PROP_BYTE_COLOR, i); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -92,7 +92,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasPCol) { MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); - MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); + const MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -103,7 +103,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasOrigSpace) { OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); - OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); + const OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -114,7 +114,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasLoopNormal) { short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); - float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); + const float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { @@ -126,7 +126,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, if (hasLoopTangent) { /* Need to do for all UV maps at some point. */ float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); - float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); + const float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; pidx++, lidx++, findex++) { @@ -154,8 +154,8 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, const int looptri_num = poly_to_tri_count(totpoly, totloop); - MPoly *mp, *mpoly; - MLoop *ml, *mloop; + const MPoly *mp, *mpoly; + const MLoop *ml, *mloop; MFace *mface, *mf; MemArena *arena = NULL; int *mface_to_poly_map; diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 5ffd253be3b..6348d83362e 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -1067,7 +1067,7 @@ void BKE_modifier_check_uuids_unique_and_report(const Object *object) BLI_gset_free(used_uuids, NULL); } -void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) +void BKE_modifier_blend_write(BlendWriter *writer, const ID *id_owner, ListBase *modbase) { if (modbase == NULL) { return; @@ -1076,7 +1076,13 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) LISTBASE_FOREACH (ModifierData *, md, modbase) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti == NULL) { - return; + continue; + } + + /* If the blendWrite callback is defined, it should handle the whole writing process. */ + if (mti->blendWrite != NULL) { + mti->blendWrite(writer, id_owner, md); + continue; } BLO_write_struct_by_name(writer, mti->structName, md); @@ -1162,10 +1168,6 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces); #endif } - - if (mti->blendWrite != NULL) { - mti->blendWrite(writer, md); - } } } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index accbca42da6..19ee2ba6605 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -2085,8 +2085,7 @@ GPUTexture *BKE_movieclip_get_gpu_texture(MovieClip *clip, MovieClipUser *cuser) /* This only means RGBA16F instead of RGBA32F. */ const bool high_bitdepth = false; const bool store_premultiplied = ibuf->rect_float ? false : true; - *tex = IMB_create_gpu_texture( - clip->id.name + 2, ibuf, high_bitdepth, store_premultiplied, false); + *tex = IMB_create_gpu_texture(clip->id.name + 2, ibuf, high_bitdepth, store_premultiplied); /* Do not generate mips for movieclips... too slow. */ GPU_texture_mipmap_mode(*tex, false, true); diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index b0e235662cb..63945f9ed42 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1021,7 +1021,7 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene) CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; Object *ob; Mesh *me; - MDisps *mdisps; + const MDisps *mdisps; MultiresModifierData *mmd; ob = ccgdm->multires.ob; @@ -1403,7 +1403,7 @@ static void multires_apply_smat(struct Depsgraph *UNUSED(depsgraph), } } -int multires_mdisp_corners(MDisps *s) +int multires_mdisp_corners(const MDisps *s) { int lvl = 13; diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index aed13adf56d..34aa90aa554 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -10,12 +10,14 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BLI_task.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_multires.h" diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e9c8b438284..cf50e8a3b43 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -60,6 +60,7 @@ #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "RNA_access.h" @@ -94,6 +95,9 @@ using blender::Stack; using blender::StringRef; using blender::Vector; using blender::VectorSet; +using blender::bke::bNodeRuntime; +using blender::bke::bNodeSocketRuntime; +using blender::bke::bNodeTreeRuntime; using blender::nodes::FieldInferencingInterface; using blender::nodes::InputSocketFieldType; using blender::nodes::NodeDeclaration; @@ -123,6 +127,7 @@ static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree, static void ntree_init_data(ID *id) { bNodeTree *ntree = (bNodeTree *)id; + ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__); ntree_set_typeinfo(ntree, nullptr); } @@ -134,6 +139,8 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* We never handle usercount here for own data. */ const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT; + ntree_dst->runtime = MEM_new<bNodeTreeRuntime>(__func__); + /* in case a running nodetree is copied */ ntree_dst->execdata = nullptr; @@ -203,9 +210,9 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* node tree will generate its own interface type */ ntree_dst->interface_type = nullptr; - if (ntree_src->field_inferencing_interface) { - ntree_dst->field_inferencing_interface = new FieldInferencingInterface( - *ntree_src->field_inferencing_interface); + if (ntree_src->runtime->field_inferencing_interface) { + ntree_dst->runtime->field_inferencing_interface = std::make_unique<FieldInferencingInterface>( + *ntree_src->runtime->field_inferencing_interface); } if (flag & LIB_ID_COPY_NO_PREVIEW) { @@ -258,8 +265,6 @@ static void ntree_free_data(ID *id) MEM_freeN(sock); } - delete ntree->field_inferencing_interface; - /* free preview hash */ if (ntree->previews) { BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free); @@ -270,6 +275,7 @@ static void ntree_free_data(ID *id) } BKE_previewimg_free(&ntree->preview); + MEM_delete(ntree->runtime); } static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) @@ -658,7 +664,7 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) BLO_read_data_address(reader, &sock->default_attribute_name); sock->total_inputs = 0; /* Clear runtime data set before drawing. */ sock->cache = nullptr; - sock->declaration = nullptr; + sock->runtime = MEM_new<bNodeSocketRuntime>(__func__); } void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) @@ -670,9 +676,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->progress = nullptr; ntree->execdata = nullptr; - ntree->runtime_flag = 0; - - ntree->field_inferencing_interface = nullptr; + ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__); BKE_ntree_update_tag_missing_runtime_data(ntree); BLO_read_data_address(reader, &ntree->adt); @@ -680,8 +684,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BLO_read_list(reader, &ntree->nodes); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + node->runtime = MEM_new<bNodeRuntime>(__func__); node->typeinfo = nullptr; - node->declaration = nullptr; BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); @@ -1117,7 +1121,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node) PointerRNA ptr; RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); - /* XXX Warning: context can be nullptr in case nodes are added in do_versions. + /* XXX WARNING: context can be nullptr in case nodes are added in do_versions. * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */ BLI_assert(C != nullptr); ntype->initfunc_api(C, &ptr); @@ -1166,7 +1170,7 @@ static void node_set_typeinfo(const struct bContext *C, } } -/* Warning: default_value must either be null or match the typeinfo at this point. +/* WARNING: default_value must either be null or match the typeinfo at this point. * This function is called both for initializing new sockets and after loading files. */ static void node_socket_set_typeinfo(bNodeTree *ntree, @@ -1512,6 +1516,7 @@ static bNodeSocket *make_socket(bNodeTree *ntree, unique_identifier_check, lb, "socket", '_', auto_identifier, sizeof(auto_identifier)); bNodeSocket *sock = MEM_cnew<bNodeSocket>("sock"); + sock->runtime = MEM_new<bNodeSocketRuntime>(__func__); sock->in_out = in_out; BLI_strncpy(sock->identifier, auto_identifier, NODE_MAXSTR); @@ -1917,6 +1922,7 @@ static void node_socket_free(bNodeSocket *sock, const bool do_id_user) } MEM_freeN(sock->default_value); } + MEM_delete(sock->runtime); } void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock) @@ -2122,6 +2128,7 @@ void nodeUniqueName(bNodeTree *ntree, bNode *node) bNode *nodeAddNode(const struct bContext *C, bNodeTree *ntree, const char *idname) { bNode *node = MEM_cnew<bNode>("new node"); + node->runtime = MEM_new<bNodeRuntime>(__func__); BLI_addtail(&ntree->nodes, node); BLI_strncpy(node->idname, idname, sizeof(node->idname)); @@ -2159,6 +2166,7 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type) static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag) { + sock_dst->runtime = MEM_new<bNodeSocketRuntime>(__func__); if (sock_src->prop) { sock_dst->prop = IDP_CopyProperty_ex(sock_src->prop, flag); } @@ -2191,6 +2199,8 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree, bNode *node_dst = (bNode *)MEM_mallocN(sizeof(bNode), __func__); *node_dst = node_src; + node_dst->runtime = MEM_new<bNodeRuntime>(__func__); + /* Can be called for nodes outside a node tree (e.g. clipboard). */ if (dst_tree) { if (unique_name) { @@ -2251,7 +2261,6 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree, } /* Reset the declaration of the new node. */ - node_dst->declaration = nullptr; nodeDeclarationEnsure(dst_tree, node_dst); return node_dst; @@ -2668,6 +2677,7 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) flag |= LIB_ID_CREATE_NO_MAIN; } bNodeTree *ntree = (bNodeTree *)BKE_libblock_alloc(bmain, ID_NT, name, flag); + BKE_libblock_init_empty(&ntree->id); if (is_embedded) { ntree->id.flag |= LIB_EMBEDDED_DATA; } @@ -2969,9 +2979,10 @@ static void node_free_node(bNodeTree *ntree, bNode *node) } if (node->typeinfo->declaration_is_dynamic) { - delete node->declaration; + delete node->runtime->declaration; } + MEM_delete(node->runtime); MEM_freeN(node); if (ntree) { @@ -3063,6 +3074,7 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree), } MEM_freeN(sock->default_value); } + MEM_delete(sock->runtime); } static void free_localized_node_groups(bNodeTree *ntree) @@ -3289,6 +3301,7 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree, } bNodeSocket *sock = MEM_cnew<bNodeSocket>("socket template"); + sock->runtime = MEM_new<bNodeSocketRuntime>(__func__); BLI_strncpy(sock->idname, stype->idname, sizeof(sock->idname)); sock->in_out = in_out; sock->type = SOCK_CUSTOM; /* int type undefined by default */ @@ -3676,34 +3689,34 @@ static void update_socket_declarations(ListBase *sockets, int index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) { const SocketDeclaration &socket_decl = *declarations[index]; - socket->declaration = &socket_decl; + socket->runtime->declaration = &socket_decl; } } void nodeSocketDeclarationsUpdate(bNode *node) { - BLI_assert(node->declaration != nullptr); - update_socket_declarations(&node->inputs, node->declaration->inputs()); - update_socket_declarations(&node->outputs, node->declaration->outputs()); + BLI_assert(node->runtime->declaration != nullptr); + update_socket_declarations(&node->inputs, node->runtime->declaration->inputs()); + update_socket_declarations(&node->outputs, node->runtime->declaration->outputs()); } bool nodeDeclarationEnsureOnOutdatedNode(bNodeTree *UNUSED(ntree), bNode *node) { - if (node->declaration != nullptr) { + if (node->runtime->declaration != nullptr) { return false; } if (node->typeinfo->declare == nullptr) { return false; } if (node->typeinfo->declaration_is_dynamic) { - node->declaration = new blender::nodes::NodeDeclaration(); - blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; + node->runtime->declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*node->runtime->declaration}; node->typeinfo->declare(builder); } else { /* Declaration should have been created in #nodeRegisterType. */ BLI_assert(node->typeinfo->fixed_declaration != nullptr); - node->declaration = node->typeinfo->fixed_declaration; + node->runtime->declaration = node->typeinfo->fixed_declaration; } return true; } @@ -4503,6 +4516,8 @@ static void registerCompositNodes() register_node_type_cmp_premulkey(); register_node_type_cmp_separate_xyz(); register_node_type_cmp_combine_xyz(); + register_node_type_cmp_separate_color(); + register_node_type_cmp_combine_color(); register_node_type_cmp_diff_matte(); register_node_type_cmp_distance_matte(); @@ -4575,6 +4590,8 @@ static void registerShaderNodes() register_node_type_sh_vect_transform(); register_node_type_sh_squeeze(); register_node_type_sh_invert(); + register_node_type_sh_sepcolor(); + register_node_type_sh_combcolor(); register_node_type_sh_seprgb(); register_node_type_sh_combrgb(); register_node_type_sh_sephsv(); @@ -4661,6 +4678,8 @@ static void registerTextureNodes() register_node_type_tex_distance(); register_node_type_tex_compose(); register_node_type_tex_decompose(); + register_node_type_tex_combine_color(); + register_node_type_tex_separate_color(); register_node_type_tex_output(); register_node_type_tex_viewer(); @@ -4822,6 +4841,7 @@ static void registerFunctionNodes() { register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); + register_node_type_fn_combine_color(); register_node_type_fn_compare(); register_node_type_fn_float_to_int(); register_node_type_fn_input_bool(); @@ -4833,6 +4853,7 @@ static void registerFunctionNodes() register_node_type_fn_random_value(); register_node_type_fn_replace_string(); register_node_type_fn_rotate_euler(); + register_node_type_fn_separate_color(); register_node_type_fn_slice_string(); register_node_type_fn_string_length(); register_node_type_fn_value_to_string(); diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 68e4cccba00..019ab114b83 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -15,6 +15,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "MOD_nodes.h" @@ -48,19 +49,19 @@ enum eNodeTreeChangedFlag { static void add_tree_tag(bNodeTree *ntree, const eNodeTreeChangedFlag flag) { - ntree->changed_flag |= flag; + ntree->runtime->changed_flag |= flag; } static void add_node_tag(bNodeTree *ntree, bNode *node, const eNodeTreeChangedFlag flag) { add_tree_tag(ntree, flag); - node->changed_flag |= flag; + node->runtime->changed_flag |= flag; } static void add_socket_tag(bNodeTree *ntree, bNodeSocket *socket, const eNodeTreeChangedFlag flag) { add_tree_tag(ntree, flag); - socket->changed_flag |= flag; + socket->runtime->changed_flag |= flag; } namespace blender::bke { @@ -172,11 +173,11 @@ static FieldInferencingInterface get_node_field_inferencing_interface(const Node /* This can happen when there is a linked node group that was not found (see T92799). */ return get_dummy_field_inferencing_interface(node); } - if (group->field_inferencing_interface == nullptr) { + if (!group->runtime->field_inferencing_interface) { /* This shouldn't happen because referenced node groups should always be updated first. */ BLI_assert_unreachable(); } - return *group->field_inferencing_interface; + return *group->runtime->field_inferencing_interface; } FieldInferencingInterface inferencing_interface; @@ -551,7 +552,8 @@ static bool update_field_inferencing(const NodeTreeRef &tree) bNodeTree &btree = *tree.btree(); /* Create new inferencing interface for this node group. */ - FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + std::unique_ptr<FieldInferencingInterface> new_inferencing_interface = + std::make_unique<FieldInferencingInterface>(); new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), InputSocketFieldType::IsSupported); new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), @@ -567,11 +569,10 @@ static bool update_field_inferencing(const NodeTreeRef &tree) update_socket_shapes(tree, field_state_by_socket_id); /* Update the previous group interface. */ - const bool group_interface_changed = btree.field_inferencing_interface == nullptr || - *btree.field_inferencing_interface != + const bool group_interface_changed = !btree.runtime->field_inferencing_interface || + *btree.runtime->field_inferencing_interface != *new_inferencing_interface; - delete btree.field_inferencing_interface; - btree.field_inferencing_interface = new_inferencing_interface; + btree.runtime->field_inferencing_interface = std::move(new_inferencing_interface); return group_interface_changed; } @@ -799,7 +800,7 @@ class NodeTreeMainUpdater { { Vector<bNodeTree *> changed_ntrees; FOREACH_NODETREE_BEGIN (bmain_, ntree, id) { - if (ntree->changed_flag != NTREE_CHANGED_NOTHING) { + if (ntree->runtime->changed_flag != NTREE_CHANGED_NOTHING) { changed_ntrees.append(ntree); } } @@ -817,7 +818,7 @@ class NodeTreeMainUpdater { if (root_ntrees.size() == 1) { bNodeTree *ntree = root_ntrees[0]; - if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) { return; } const TreeUpdateResult result = this->update_tree(*ntree); @@ -830,7 +831,7 @@ class NodeTreeMainUpdater { if (!is_single_tree_update) { Vector<bNodeTree *> ntrees_in_order = this->get_tree_update_order(root_ntrees); for (bNodeTree *ntree : ntrees_in_order) { - if (ntree->changed_flag == NTREE_CHANGED_NOTHING) { + if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) { continue; } if (!update_result_by_tree_.contains(ntree)) { @@ -1002,7 +1003,8 @@ class NodeTreeMainUpdater { ntreeTexCheckCyclics(&ntree); } - if (ntree.changed_flag & NTREE_CHANGED_INTERFACE || ntree.changed_flag & NTREE_CHANGED_ANY) { + if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE || + ntree.runtime->changed_flag & NTREE_CHANGED_ANY) { result.interface_changed = true; } @@ -1057,18 +1059,18 @@ class NodeTreeMainUpdater { this->ensure_tree_ref(ntree, tree_ref); const NodeRef &node = *tree_ref->find_node(*bnode); if (this->should_update_individual_node(node)) { - const uint32_t old_changed_flag = ntree.changed_flag; - ntree.changed_flag = NTREE_CHANGED_NOTHING; + const uint32_t old_changed_flag = ntree.runtime->changed_flag; + ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING; - /* This may set #ntree.changed_flag which is detected below. */ + /* This may set #ntree.runtime->changed_flag which is detected below. */ this->update_individual_node(node); - if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) { /* The tree ref is outdated and needs to be rebuilt. Generally, only very few update * functions change the node. Typically zero or one nodes change after an update. */ tree_ref.reset(); } - ntree.changed_flag |= old_changed_flag; + ntree.runtime->changed_flag |= old_changed_flag; } } } @@ -1077,13 +1079,13 @@ class NodeTreeMainUpdater { { bNodeTree &ntree = *node.btree(); bNode &bnode = *node.bnode(); - if (ntree.changed_flag & NTREE_CHANGED_ANY) { + if (ntree.runtime->changed_flag & NTREE_CHANGED_ANY) { return true; } - if (bnode.changed_flag & NTREE_CHANGED_NODE_PROPERTY) { + if (bnode.runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) { return true; } - if (ntree.changed_flag & NTREE_CHANGED_LINK) { + if (ntree.runtime->changed_flag & NTREE_CHANGED_LINK) { /* Node groups currently always rebuilt their sockets when they are updated. * So avoid calling the update method when no new link was added to it. */ if (node.is_group_input_node()) { @@ -1101,7 +1103,7 @@ class NodeTreeMainUpdater { return true; } } - if (ntree.changed_flag & NTREE_CHANGED_INTERFACE) { + if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) { if (node.is_group_input_node() || node.is_group_output_node()) { return true; } @@ -1232,16 +1234,16 @@ class NodeTreeMainUpdater { } /* Reset the changed_flag to allow detecting when the update callback changed the node tree. */ - const uint32_t old_changed_flag = ntree.changed_flag; - ntree.changed_flag = NTREE_CHANGED_NOTHING; + const uint32_t old_changed_flag = ntree.runtime->changed_flag; + ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING; ntree.typeinfo->update(&ntree); - if (ntree.changed_flag != NTREE_CHANGED_NOTHING) { + if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) { /* The tree ref is outdated and needs to be rebuilt. */ tree_ref.reset(); } - ntree.changed_flag |= old_changed_flag; + ntree.runtime->changed_flag |= old_changed_flag; } void remove_unused_previews_when_necessary(bNodeTree &ntree) @@ -1250,7 +1252,7 @@ class NodeTreeMainUpdater { const uint32_t allowed_flags = NTREE_CHANGED_LINK | NTREE_CHANGED_SOCKET_PROPERTY | NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT | NTREE_CHANGED_INTERFACE; - if ((ntree.changed_flag & allowed_flags) == ntree.changed_flag) { + if ((ntree.runtime->changed_flag & allowed_flags) == ntree.runtime->changed_flag) { return; } BKE_node_preview_remove_unused(&ntree); @@ -1259,7 +1261,7 @@ class NodeTreeMainUpdater { void propagate_runtime_flags(const NodeTreeRef &tree_ref) { bNodeTree &ntree = *tree_ref.btree(); - ntree.runtime_flag = 0; + ntree.runtime->runtime_flag = 0; if (ntree.type != NTREE_SHADER) { return; } @@ -1268,7 +1270,7 @@ class NodeTreeMainUpdater { for (const NodeRef *group_node : tree_ref.nodes_by_type("NodeGroup")) { const bNodeTree *group = reinterpret_cast<bNodeTree *>(group_node->bnode()->id); if (group != nullptr) { - ntree.runtime_flag |= group->runtime_flag; + ntree.runtime->runtime_flag |= group->runtime->runtime_flag; } } /* Check if the tree itself has an animated image. */ @@ -1276,7 +1278,7 @@ class NodeTreeMainUpdater { for (const NodeRef *node : tree_ref.nodes_by_type(idname)) { Image *image = reinterpret_cast<Image *>(node->bnode()->id); if (image != nullptr && BKE_image_is_animated(image)) { - ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION; + ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION; break; } } @@ -1288,7 +1290,7 @@ class NodeTreeMainUpdater { "ShaderNodeOutputAOV"}) { const Span<const NodeRef *> nodes = tree_ref.nodes_by_type(idname); if (!nodes.is_empty()) { - ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT; + ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT; break; } } @@ -1323,12 +1325,12 @@ class NodeTreeMainUpdater { /* Compute a hash that represents the node topology connected to the output. This always has to * be updated even if it is not used to detect changes right now. Otherwise - * #btree.output_topology_hash will go out of date. */ + * #btree.runtime.output_topology_hash will go out of date. */ const Vector<const SocketRef *> tree_output_sockets = this->find_output_sockets(tree); - const uint32_t old_topology_hash = btree.output_topology_hash; + const uint32_t old_topology_hash = btree.runtime->output_topology_hash; const uint32_t new_topology_hash = this->get_combined_socket_topology_hash( tree, tree_output_sockets); - btree.output_topology_hash = new_topology_hash; + btree.runtime->output_topology_hash = new_topology_hash; if (const AnimData *adt = BKE_animdata_from_id(&btree.id)) { /* Drivers may copy values in the node tree around arbitrarily and may cause the output to @@ -1352,7 +1354,7 @@ class NodeTreeMainUpdater { } } - if (btree.changed_flag & NTREE_CHANGED_ANY) { + if (btree.runtime->changed_flag & NTREE_CHANGED_ANY) { return true; } @@ -1361,8 +1363,8 @@ class NodeTreeMainUpdater { } /* The topology hash can only be used when only topology-changing operations have been done. */ - if (btree.changed_flag == - (btree.changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) { + if (btree.runtime->changed_flag == + (btree.runtime->changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) { if (old_topology_hash == new_topology_hash) { return false; } @@ -1404,7 +1406,7 @@ class NodeTreeMainUpdater { if (bnode.type == NODE_GROUP) { const bNodeTree *node_group = reinterpret_cast<const bNodeTree *>(bnode.id); if (node_group != nullptr && - node_group->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) { + node_group->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) { return true; } } @@ -1540,12 +1542,12 @@ class NodeTreeMainUpdater { const NodeRef &node = in_out_socket.node(); const bNode &bnode = *node.bnode(); const bNodeSocket &bsocket = *in_out_socket.bsocket(); - if (bsocket.changed_flag != NTREE_CHANGED_NOTHING) { + if (bsocket.runtime->changed_flag != NTREE_CHANGED_NOTHING) { return true; } - if (bnode.changed_flag != NTREE_CHANGED_NOTHING) { + if (bnode.runtime->changed_flag != NTREE_CHANGED_NOTHING) { const bool only_unused_internal_link_changed = (bnode.flag & NODE_MUTED) == 0 && - bnode.changed_flag == + bnode.runtime->changed_flag == NTREE_CHANGED_INTERNAL_LINK; if (!only_unused_internal_link_changed) { return true; @@ -1591,15 +1593,15 @@ class NodeTreeMainUpdater { void reset_changed_flags(bNodeTree &ntree) { - ntree.changed_flag = NTREE_CHANGED_NOTHING; + ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING; LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { - node->changed_flag = NTREE_CHANGED_NOTHING; + node->runtime->changed_flag = NTREE_CHANGED_NOTHING; node->update = 0; LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - socket->changed_flag = NTREE_CHANGED_NOTHING; + socket->runtime->changed_flag = NTREE_CHANGED_NOTHING; } LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - socket->changed_flag = NTREE_CHANGED_NOTHING; + socket->runtime->changed_flag = NTREE_CHANGED_NOTHING; } } } diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 2a25d73ed87..55b9951c52d 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -583,7 +583,7 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre } BKE_particle_system_blend_write(writer, &ob->particlesystem); - BKE_modifier_blend_write(writer, &ob->modifiers); + BKE_modifier_blend_write(writer, &ob->id, &ob->modifiers); BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); BKE_shaderfx_blend_write(writer, &ob->shader_fx); @@ -2114,7 +2114,7 @@ static const char *get_obdata_defname(int type) case OB_SPEAKER: return DATA_("Speaker"); case OB_CURVES: - return DATA_("HairCurves"); + return DATA_("Curves"); case OB_POINTCLOUD: return DATA_("PointCloud"); case OB_VOLUME: diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 4dc0130366e..dec9a594938 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -48,7 +48,7 @@ static float nextfr(RNG *rng, float min, float max) static float gaussRand(RNG *rng) { /* NOTE: to avoid numerical problems with very small numbers, we make these variables - * singe-precision floats, but later we call the double-precision log() and sqrt() functions + * single-precision floats, but later we call the double-precision log() and sqrt() functions * instead of logf() and sqrtf(). */ float x; float y; diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index e7ed100ed03..7c96c463339 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -237,14 +237,14 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose) for (ima = bmain->images.first; ima; ima = ima->id.next) { if (BKE_image_has_packedfile(ima) == false && !ID_IS_LINKED(ima)) { - if (ima->source == IMA_SRC_FILE) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_TILED)) { BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot++; } - else if (BKE_image_has_multiple_ibufs(ima) && verbose) { + else if (ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE) && verbose) { BKE_reportf(reports, RPT_WARNING, - "Image '%s' skipped, movies, image sequences and packed files not supported", + "Image '%s' skipped, packing movies or image sequences not supported", ima->id.name + 2); } } @@ -494,15 +494,22 @@ static void unpack_generate_paths(const char *name, if (tempname[0] == '\0') { /* NOTE: we generally do not have any real way to re-create extension out of data. */ - BLI_strncpy(tempname, id->name + 2, sizeof(tempname)); + const size_t len = BLI_strncpy_rlen(tempname, id->name + 2, sizeof(tempname)); printf("%s\n", tempname); - /* For images we can add the file extension based on the file magic. */ + /* For images ensure that the temporary filename contains tile number information as well as + * a file extension based on the file magic. */ if (id_type == ID_IM) { - ImagePackedFile *imapf = ((Image *)id)->packedfiles.last; + Image *ima = (Image *)id; + ImagePackedFile *imapf = ima->packedfiles.last; if (imapf != NULL && imapf->packedfile != NULL) { const PackedFile *pf = imapf->packedfile; enum eImbFileType ftype = IMB_ispic_type_from_memory((const uchar *)pf->data, pf->size); + if (ima->source == IMA_SRC_TILED) { + char tile_number[6]; + BLI_snprintf(tile_number, sizeof(tile_number), ".%d", imapf->tile_number); + BLI_strncpy(tempname + len, tile_number, sizeof(tempname) - len); + } if (ftype != IMB_FTYPE_NONE) { const int imtype = BKE_ftype_to_imtype(ftype, NULL); BKE_image_path_ensure_ext_from_imtype(tempname, imtype); @@ -639,6 +646,11 @@ int BKE_packedfile_unpack_image(Main *bmain, /* keep the new name in the image for non-pack specific reasons */ if (how != PF_REMOVE) { BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath)); + if (ima->source == IMA_SRC_TILED) { + /* Ensure that the Image filepath is kept in a tokenized format. */ + char *filename = (char *)BLI_path_basename(ima->filepath); + BKE_image_ensure_tile_token(filename); + } } MEM_freeN(new_file_path); } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 0f523d87d9b..cff7eb20b05 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -606,6 +606,11 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) Brush *BKE_paint_brush(Paint *p) { + return (Brush *)BKE_paint_brush_for_read((const Paint *)p); +} + +const Brush *BKE_paint_brush_for_read(const Paint *p) +{ return p ? p->brush : NULL; } @@ -2051,7 +2056,7 @@ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } @@ -2066,7 +2071,7 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b935f2afaaa..a5f7f73af70 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -1667,7 +1667,7 @@ void psys_interpolate_face(Mesh *mesh, const float (*vert_normals)[3], MFace *mface, MTFace *tface, - float (*orcodata)[3], + const float (*orcodata)[3], float w[4], float vec[3], float nor[3], @@ -1680,7 +1680,7 @@ void psys_interpolate_face(Mesh *mesh, float *uv1, *uv2, *uv3, *uv4; float n1[3], n2[3], n3[3], n4[3]; float tuv[4][2]; - float *o1, *o2, *o3, *o4; + const float *o1, *o2, *o3, *o4; v1 = mvert[mface->v1].co; v2 = mvert[mface->v2].co; @@ -1903,9 +1903,10 @@ int psys_particle_dm_face_lookup(Mesh *mesh_final, struct LinkNode **poly_nodes) { MFace *mtessface_final; - OrigSpaceFace *osface_final; + const OrigSpaceFace *osface_final; int pindex_orig; - float uv[2], (*faceuv)[2]; + float uv[2]; + const float(*faceuv)[2]; const int *index_mf_to_mpoly_deformed = NULL; const int *index_mf_to_mpoly = NULL; @@ -2048,11 +2049,7 @@ static int psys_map_index_on_dm(Mesh *mesh, } else { /* FROM_FACE/FROM_VOLUME */ /* find a face on the derived mesh that uses this face */ - MFace *mface; - OrigSpaceFace *osface; - int i; - - i = index_dmcache; + int i = index_dmcache; if (i == DMCACHE_NOTFOUND || i >= mesh->totface) { return 0; @@ -2062,8 +2059,8 @@ static int psys_map_index_on_dm(Mesh *mesh, /* modify the original weights to become * weights for the derived mesh face */ - osface = CustomData_get_layer(&mesh->fdata, CD_ORIGSPACE); - mface = &mesh->mface[i]; + OrigSpaceFace *osface = CustomData_get_layer(&mesh->fdata, CD_ORIGSPACE); + const MFace *mface = &mesh->mface[i]; if (osface == NULL) { mapfw[0] = mapfw[1] = mapfw[2] = mapfw[3] = 0.0f; @@ -2090,7 +2087,7 @@ void psys_particle_on_dm(Mesh *mesh_final, float orco[3]) { float tmpnor[3], mapfw[4]; - float(*orcodata)[3]; + const float(*orcodata)[3]; int mapindex; if (!psys_map_index_on_dm( @@ -3628,7 +3625,7 @@ static void psys_cache_edit_paths_iter(void *__restrict iter_data_v, BKE_defvert_weight_to_rgb(ca->col, pind.hkey[1]->weight); } else { - /* warning: copied from 'do_particle_interpolation' (without 'mvert' array stepping) */ + /* WARNING: copied from 'do_particle_interpolation' (without 'mvert' array stepping) */ float real_t; if (result.time < 0.0f) { real_t = -result.time; @@ -3796,7 +3793,7 @@ void psys_get_from_key(ParticleKey *key, float loc[3], float vel[3], float rot[4 } } -static void triatomat(float *v1, float *v2, float *v3, float (*uv)[2], float mat[4][4]) +static void triatomat(float *v1, float *v2, float *v3, const float (*uv)[2], float mat[4][4]) { float det, w1, w2, d1[2], d2[2]; @@ -3842,8 +3839,7 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4] { float v[3][3]; MFace *mface; - OrigSpaceFace *osface; - float(*orcodata)[3]; + const float(*orcodata)[3]; int i = (ELEM(pa->num_dmcache, DMCACHE_ISCHILD, DMCACHE_NOTFOUND)) ? pa->num : pa->num_dmcache; if (i == -1 || i >= mesh->totface) { @@ -3852,7 +3848,7 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4] } mface = &mesh->mface[i]; - osface = CustomData_get(&mesh->fdata, i, CD_ORIGSPACE); + const OrigSpaceFace *osface = CustomData_get(&mesh->fdata, i, CD_ORIGSPACE); if (orco && (orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO))) { copy_v3_v3(v[0], orcodata[mface->v1]); @@ -4159,7 +4155,7 @@ static int get_particle_uv(Mesh *mesh, bool from_vert) { MFace *mf; - MTFace *tf; + const MTFace *tf; int i; tf = CustomData_get_layer_named(&mesh->fdata, CD_MTFACE, name); @@ -5039,7 +5035,6 @@ void psys_get_dupli_texture(ParticleSystem *psys, float uv[2], float orco[3]) { - MFace *mface; float loc[3]; int num; @@ -5063,9 +5058,9 @@ void psys_get_dupli_texture(ParticleSystem *psys, const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); if (uv_idx >= 0) { - MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); + const MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); if (mtface != NULL) { - mface = CustomData_get(&psmd->mesh_final->fdata, cpa->num, CD_MFACE); + const MFace *mface = CustomData_get(&psmd->mesh_final->fdata, cpa->num, CD_MFACE); mtface += cpa->num; psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv); } @@ -5107,8 +5102,8 @@ void psys_get_dupli_texture(ParticleSystem *psys, const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); if (uv_idx >= 0) { - MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); - mface = CustomData_get(&psmd->mesh_final->fdata, num, CD_MFACE); + const MTFace *mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); + const MFace *mface = CustomData_get(&psmd->mesh_final->fdata, num, CD_MFACE); mtface += num; psys_interpolate_uvs(mtface, mface->v4, pa->fuv, uv); } diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 244396af6e6..4be48efb2b5 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -808,7 +808,7 @@ static void exec_distribute_child(TaskPool *__restrict UNUSED(pool), void *taskd static int distribute_compare_orig_index(const void *p1, const void *p2, void *user_data) { - int *orig_index = (int *)user_data; + const int *orig_index = (const int *)user_data; int index1 = orig_index[*(const int *)p1]; int index2 = orig_index[*(const int *)p2]; @@ -989,7 +989,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, if (from == PART_FROM_VERT) { MVert *mv = mesh->mvert; - float(*orcodata)[3] = CustomData_get_layer(&mesh->vdata, CD_ORCO); + const float(*orcodata)[3] = CustomData_get_layer(&mesh->vdata, CD_ORCO); int totvert = mesh->totvert; tree = BLI_kdtree_3d_new(totvert); @@ -1037,7 +1037,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, if ((part->flag & PART_EDISTR || children) && from != PART_FROM_VERT) { MVert *v1, *v2, *v3, *v4; float totarea = 0.0f, co1[3], co2[3], co3[3], co4[3]; - float(*orcodata)[3]; + const float(*orcodata)[3]; orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO); @@ -1219,7 +1219,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, /* For hair, sort by origindex (allows optimization's in rendering), */ /* however with virtual parents the children need to be in random order. */ if (part->type == PART_HAIR && !(part->childtype == PART_CHILD_FACES && part->parents != 0.0f)) { - int *orig_index = NULL; + const int *orig_index = NULL; if (from == PART_FROM_VERT) { if (mesh->totvert) { @@ -1233,8 +1233,11 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx, } if (orig_index) { - BLI_qsort_r( - particle_element, totpart, sizeof(int), distribute_compare_orig_index, orig_index); + BLI_qsort_r(particle_element, + totpart, + sizeof(int), + distribute_compare_orig_index, + (void *)orig_index); } } diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 7fdc60a265b..a7ab1536b1f 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -321,8 +321,9 @@ void psys_calc_dmcache(Object *ob, Mesh *mesh_final, Mesh *mesh_original, Partic if (!mesh_final->runtime.deformed_only) { /* Will use later to speed up subsurf/evaluated mesh. */ LinkNode *node, *nodedmelem, **nodearray; - int totdmelem, totelem, i, *origindex, *origindex_poly = NULL; - + int totdmelem, totelem, i; + const int *origindex; + const int *origindex_poly = NULL; if (psys->part->from == PART_FROM_VERT) { totdmelem = mesh_final->totvert; diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index c7c8fbe8bce..a86663a9c74 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -10,6 +10,11 @@ extern "C" { #endif +struct MLoop; +struct MLoopTri; +struct MPoly; +struct MVert; + /* Axis-aligned bounding box */ typedef struct { float bmin[3], bmax[3]; @@ -141,12 +146,12 @@ struct PBVH { /* Mesh data */ const struct Mesh *mesh; - /* Note: Normals are not const because they can be updated for drawing by sculpt code. */ + /* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */ float (*vert_normals)[3]; - MVert *verts; - const MPoly *mpoly; - const MLoop *mloop; - const MLoopTri *looptri; + struct MVert *verts; + const struct MPoly *mpoly; + const struct MLoop *mloop; + const struct MLoopTri *looptri; CustomData *vdata; CustomData *ldata; CustomData *pdata; diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index 5623cac44ac..49397797c0d 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -71,7 +71,7 @@ static void extract_barycentric_pixels(UDIMTilePixels &tile_data, int x; for (x = minx; x < maxx; x++) { - float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y); + float2 uv((float(x) + 0.5f) / image_buffer->x, (float(y) + 0.5f) / image_buffer->y); float3 barycentric_weights; barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights); @@ -108,7 +108,7 @@ struct EncodePixelsUserData { ImageUser *image_user; PBVH *pbvh; Vector<PBVHNode *> *nodes; - MLoopUV *ldata_uv; + const MLoopUV *ldata_uv; }; static void do_encode_pixels(void *__restrict userdata, @@ -283,7 +283,8 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image return; } - MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); + const MLoopUV *ldata_uv = static_cast<const MLoopUV *>( + CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { return; } @@ -307,6 +308,12 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image apply_watertight_check(pbvh, image, image_user); } + /* Rebuild the undo regions. */ + for (PBVHNode *node : nodes_to_update) { + NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); + node_data->rebuild_undo_regions(); + } + /* Clear the UpdatePixels flag. */ for (PBVHNode *node : nodes_to_update) { node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 353f89068d8..2467ca16670 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -1450,7 +1450,7 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) { PTCacheFile *pf; FILE *fp = NULL; - char filename[MAX_PTCACHE_FILE]; + char filepath[MAX_PTCACHE_FILE]; #ifndef DURIAN_POINTCACHE_LIB_OK /* don't allow writing for linked objects */ @@ -1465,20 +1465,20 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) } } - ptcache_filename(pid, filename, cfra, true, true); + ptcache_filename(pid, filepath, cfra, true, true); if (mode == PTCACHE_FILE_READ) { - fp = BLI_fopen(filename, "rb"); + fp = BLI_fopen(filepath, "rb"); } else if (mode == PTCACHE_FILE_WRITE) { /* Will create the dir if needs be, same as "//textures" is created. */ - BLI_make_existing_file(filename); + BLI_make_existing_file(filepath); - fp = BLI_fopen(filename, "wb"); + fp = BLI_fopen(filepath, "wb"); } else if (mode == PTCACHE_FILE_UPDATE) { - BLI_make_existing_file(filename); - fp = BLI_fopen(filename, "rb+"); + BLI_make_existing_file(filepath); + fp = BLI_fopen(filepath, "rb+"); } if (!fp) { diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 3ee46fc4f15..9720c61e3b9 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -20,6 +20,7 @@ #include "BLI_string.h" #include "BLI_task.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_anim_data.h" #include "BKE_customdata.h" @@ -44,6 +45,7 @@ using blender::float3; using blender::IndexRange; using blender::Span; +using blender::Vector; /* PointCloud datablock */ @@ -107,27 +109,25 @@ static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_a { PointCloud *pointcloud = (PointCloud *)id; - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare( - &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + Vector<CustomDataLayer, 16> point_layers; + CustomData_blend_write_prepare(pointcloud->pdata, point_layers); /* Write LibData */ BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); BKE_id_blend_write(writer, &pointcloud->id); /* Direct data */ - CustomData_blend_write( - writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); + CustomData_blend_write(writer, + &pointcloud->pdata, + point_layers, + pointcloud->totpoint, + CD_MASK_ALL, + &pointcloud->id); BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); if (pointcloud->adt) { BKE_animdata_blend_write(writer, pointcloud->adt); } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } } static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 864a4f3281b..de5589cf5dc 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -264,6 +264,14 @@ bSound *BKE_sound_new_file(Main *bmain, const char *filepath) BLI_strncpy(sound->filepath, filepath, FILE_MAX); /* sound->type = SOUND_TYPE_FILE; */ /* XXX unused currently */ + /* Extract sound specs for bSound */ + SoundInfo info; + bool success = BKE_sound_info_get(bmain, sound, &info); + if (success) { + sound->samplerate = info.specs.samplerate; + sound->audio_channels = info.specs.channels; + } + sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock"); BLI_spin_init(sound->spinlock); @@ -1202,6 +1210,7 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; + sound_info->specs.samplerate = info.specs.rate; return true; } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 7704a74841a..e8c7aff75d1 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -116,15 +116,15 @@ void Spline::reverse() this->mark_cache_invalid(); } -int Spline::evaluated_edges_size() const +int Spline::evaluated_edges_num() const { - const int eval_size = this->evaluated_points_size(); - if (eval_size < 2) { + const int eval_num = this->evaluated_points_num(); + if (eval_num < 2) { /* Two points are required for an edge. */ return 0; } - return this->is_cyclic_ ? eval_size : eval_size - 1; + return this->is_cyclic_ ? eval_num : eval_num - 1; } float Spline::length() const @@ -133,11 +133,11 @@ float Spline::length() const return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last(); } -int Spline::segments_size() const +int Spline::segments_num() const { - const int size = this->size(); + const int num = this->size(); - return is_cyclic_ ? size : size - 1; + return is_cyclic_ ? num : num - 1; } bool Spline::is_cyclic() const @@ -177,7 +177,7 @@ Span<float> Spline::evaluated_lengths() const return evaluated_lengths_cache_; } - const int total = evaluated_edges_size(); + const int total = evaluated_edges_num(); evaluated_lengths_cache_.resize(total); if (total != 0) { Span<float3> positions = this->evaluated_positions(); @@ -242,8 +242,8 @@ Span<float3> Spline::evaluated_tangents() const return evaluated_tangents_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_tangents_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_tangents_cache_.resize(eval_num); Span<float3> positions = this->evaluated_positions(); @@ -369,8 +369,8 @@ Span<float3> Spline::evaluated_normals() const return evaluated_normals_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_normals_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_normals_cache_.resize(eval_num); Span<float3> tangents = this->evaluated_tangents(); MutableSpan<float3> normals = evaluated_normals_cache_; @@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); const int index = offset - lengths.begin(); - const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1; + const int next_index = (index == this->evaluated_points_num() - 1) ? 0 : index + 1; const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; const float length_in_segment = length - previous_length; @@ -420,30 +420,30 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const return LookupResult{index, next_index, factor}; } -Array<float> Spline::sample_uniform_index_factors(const int samples_size) const +Array<float> Spline::sample_uniform_index_factors(const int samples_num) const { const Span<float> lengths = this->evaluated_lengths(); - BLI_assert(samples_size > 0); - Array<float> samples(samples_size); + BLI_assert(samples_num > 0); + Array<float> samples(samples_num); samples[0] = 0.0f; - if (samples_size == 1) { + if (samples_num == 1) { return samples; } const float total_length = this->length(); - const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1)); + const float sample_length = total_length / (samples_num - (is_cyclic_ ? 0 : 1)); /* Store the length at the previous evaluated point in a variable so it can * start out at zero (the lengths array doesn't contain 0 for the first point). */ float prev_length = 0.0f; int i_sample = 1; - for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) { + for (const int i_evaluated : IndexRange(this->evaluated_edges_num())) { const float length = lengths[i_evaluated]; /* Add every sample that fits in this evaluated edge. */ - while ((sample_length * i_sample) < length && i_sample < samples_size) { + while ((sample_length * i_sample) < length && i_sample < samples_num) { const float factor = (sample_length * i_sample - prev_length) / (length - prev_length); samples[i_sample] = i_evaluated + factor; i_sample++; @@ -454,8 +454,8 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const /* Zero lengths or float inaccuracies can cause invalid values, or simply * skip some, so set the values that weren't completed in the main loop. */ - for (const int i : IndexRange(i_sample, samples_size - i_sample)) { - samples[i] = float(samples_size); + for (const int i : IndexRange(i_sample, samples_num - i_sample)) { + samples[i] = float(samples_num); } if (!is_cyclic_) { @@ -468,23 +468,23 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const { - const int eval_size = this->evaluated_points_size(); + const int eval_num = this->evaluated_points_num(); if (is_cyclic_) { - if (index_factor < eval_size) { + if (index_factor < eval_num) { const int index = std::floor(index_factor); - const int next_index = (index < eval_size - 1) ? index + 1 : 0; + const int next_index = (index < eval_num - 1) ? index + 1 : 0; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 1, 0, 1.0f}; + return LookupResult{eval_num - 1, 0, 1.0f}; } - if (index_factor < eval_size - 1) { + if (index_factor < eval_num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return LookupResult{index, next_index, index_factor - index}; } - return LookupResult{eval_size - 2, eval_size - 1, 1.0f}; + return LookupResult{eval_num - 2, eval_num - 1, 1.0f}; } void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const @@ -504,7 +504,7 @@ void Spline::sample_with_index_factors(const GVArray &src, Span<float> index_factors, GMutableSpan dst) const { - BLI_assert(src.size() == this->evaluated_points_size()); + BLI_assert(src.size() == this->evaluated_points_num()); blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 8e207f93bf5..80515d0ef37 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -335,7 +335,7 @@ void BezierSpline::mark_cache_invalid() auto_handles_dirty_ = true; } -int BezierSpline::evaluated_points_size() const +int BezierSpline::evaluated_points_num() const { BLI_assert(this->size() > 0); return this->control_point_offsets().last(); @@ -502,12 +502,12 @@ Span<float> BezierSpline::evaluated_mappings() const return evaluated_mapping_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_mapping_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_mapping_cache_.resize(eval_num); MutableSpan<float> mappings = evaluated_mapping_cache_; - if (eval_size == 1) { + if (eval_num == 1) { mappings.first() = 0.0f; mapping_cache_dirty_ = false; return mappings; @@ -517,7 +517,7 @@ Span<float> BezierSpline::evaluated_mappings() const blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings); + calculate_mappings_linear_resolution(offsets, num, resolution_, is_cyclic_, mappings); }); mapping_cache_dirty_ = false; @@ -535,15 +535,15 @@ Span<float3> BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); MutableSpan<float3> positions = evaluated_position_cache_; - if (size == 1) { + if (num == 1) { /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ - BLI_assert(eval_size == 1); + BLI_assert(eval_num == 1); positions.first() = positions_.first(); position_cache_dirty_ = false; return positions; @@ -556,7 +556,7 @@ Span<float3> BezierSpline::evaluated_positions() const const int grain_size = std::max(512 / resolution_, 1); blender::threading::isolate_task([&]() { /* Isolate the task, since this is function is multi-threaded and holds a lock. */ - blender::threading::parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) { + blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) { for (const int i : range) { this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i])); } @@ -564,7 +564,7 @@ Span<float3> BezierSpline::evaluated_positions() const }); if (is_cyclic_) { this->evaluate_segment( - size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1])); + num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 1])); } else { /* Since evaluating the bezier segment doesn't add the final point, @@ -579,23 +579,23 @@ Span<float3> BezierSpline::evaluated_positions() const BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor( const float index_factor) const { - const int size = this->size(); + const int num = this->size(); if (is_cyclic_) { - if (index_factor < size) { + if (index_factor < num) { const int index = std::floor(index_factor); - const int next_index = (index < size - 1) ? index + 1 : 0; + const int next_index = (index < num - 1) ? index + 1 : 0; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 1, 0, 1.0f}; + return InterpolationData{num - 1, 0, 1.0f}; } - if (index_factor < size - 1) { + if (index_factor < num - 1) { const int index = std::floor(index_factor); const int next_index = index + 1; return InterpolationData{index, next_index, index_factor - index}; } - return InterpolationData{size - 2, size - 1, 1.0f}; + return InterpolationData{num - 2, num - 1, 1.0f}; } /* Use a spline argument to avoid adding this to the header. */ @@ -605,7 +605,7 @@ static void interpolate_to_evaluated_impl(const BezierSpline &spline, MutableSpan<T> dst) { BLI_assert(src.size() == spline.size()); - BLI_assert(dst.size() == spline.evaluated_points_size()); + BLI_assert(dst.size() == spline.evaluated_points_num()); Span<float> mappings = spline.evaluated_mappings(); for (const int i : dst.index_range()) { @@ -627,8 +627,8 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const return src; } - const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + const int eval_num = this->evaluated_points_num(); + if (eval_num == 1) { return src; } @@ -636,7 +636,7 @@ GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(eval_size); + Array<T> values(eval_num); interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 9d1d5a53a43..a7eeb82d854 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -124,19 +124,19 @@ void NURBSpline::mark_cache_invalid() length_cache_dirty_ = true; } -int NURBSpline::evaluated_points_size() const +int NURBSpline::evaluated_points_num() const { - if (!this->check_valid_size_and_order()) { + if (!this->check_valid_num_and_order()) { return 0; } - return resolution_ * this->segments_size(); + return resolution_ * this->segments_num(); } void NURBSpline::correct_end_tangents() const { } -bool NURBSpline::check_valid_size_and_order() const +bool NURBSpline::check_valid_num_and_order() const { if (this->size() < order_) { return false; @@ -152,10 +152,10 @@ bool NURBSpline::check_valid_size_and_order() const return true; } -int NURBSpline::knots_size() const +int NURBSpline::knots_num() const { - const int size = this->size() + order_; - return is_cyclic_ ? size + order_ - 1 : size; + const int num = this->size() + order_; + return is_cyclic_ ? num + order_ - 1 : num; } void NURBSpline::calculate_knots() const @@ -173,7 +173,7 @@ void NURBSpline::calculate_knots() const * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); - knots_.resize(this->knots_size()); + knots_.resize(this->knots_num()); MutableSpan<float> knots = knots_; int r = head; @@ -203,13 +203,13 @@ void NURBSpline::calculate_knots() const Span<float> NURBSpline::knots() const { if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } std::lock_guard lock{knots_mutex_}; if (!knots_dirty_) { - BLI_assert(knots_.size() == this->knots_size()); + BLI_assert(knots_.size() == this->knots_num()); return knots_; } @@ -221,7 +221,7 @@ Span<float> NURBSpline::knots() const } static void calculate_basis_for_point(const float parameter, - const int size, + const int num, const int degree, const Span<float> knots, MutableSpan<float> r_weights, @@ -231,7 +231,7 @@ static void calculate_basis_for_point(const float parameter, int start = 0; int end = 0; - for (const int i : IndexRange(size + degree)) { + for (const int i : IndexRange(num + degree)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) { continue; @@ -248,7 +248,7 @@ static void calculate_basis_for_point(const float parameter, for (const int i_order : IndexRange(2, degree)) { if (end + i_order >= knots.size()) { - end = size + degree - i_order; + end = num + degree - i_order; } for (const int i : IndexRange(end - start + 1)) { const int knot_index = start + i; @@ -284,16 +284,16 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const return basis_cache_; } - const int size = this->size(); - const int eval_size = this->evaluated_points_size(); + const int num = this->size(); + const int eval_num = this->evaluated_points_num(); const int order = this->order(); const int degree = order - 1; - basis_cache_.weights.resize(eval_size * order); - basis_cache_.start_indices.resize(eval_size); + basis_cache_.weights.resize(eval_num * order); + basis_cache_.start_indices.resize(eval_num); - if (eval_size == 0) { + if (eval_num == 0) { return basis_cache_; } @@ -303,14 +303,14 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const const Span<float> control_weights = this->weights(); const Span<float> knots = this->knots(); - const int last_control_point_index = is_cyclic_ ? size + degree : size; + const int last_control_point_index = is_cyclic_ ? num + degree : num; const float start = knots[degree]; const float end = knots[last_control_point_index]; - const float step = (end - start) / this->evaluated_edges_size(); - for (const int i : IndexRange(eval_size)) { + const float step = (end - start) / this->evaluated_edges_num(); + for (const int i : IndexRange(eval_num)) { /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[size + degree]); + const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]); MutableSpan<float> point_weights = basis_weights.slice(i * order, order); @@ -318,7 +318,7 @@ const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); for (const int j : point_weights.index_range()) { - const int point_index = (basis_start_indices[i] + j) % size; + const int point_index = (basis_start_indices[i] + j) % num; point_weights[j] *= control_weights[point_index]; } } @@ -333,7 +333,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const blender::VArray<T> &src, MutableSpan<T> dst) { - const int size = src.size(); + const int num = src.size(); blender::attribute_math::DefaultMixer<T> mixer(dst); for (const int i : dst.index_range()) { @@ -341,7 +341,7 @@ void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache, const int start_index = basis_cache.start_indices[i]; for (const int j : point_weights.index_range()) { - const int point_index = (start_index + j) % size; + const int point_index = (start_index + j) % num; mixer.mix_in(i, src[point_index], point_weights[j]); } } @@ -363,7 +363,7 @@ GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) { - Array<T> values(this->evaluated_points_size()); + Array<T> values(this->evaluated_points_num()); interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values); new_varray = VArray<T>::ForContainer(std::move(values)); } @@ -383,8 +383,8 @@ Span<float3> NURBSpline::evaluated_positions() const return evaluated_position_cache_; } - const int eval_size = this->evaluated_points_size(); - evaluated_position_cache_.resize(eval_size); + const int eval_num = this->evaluated_points_num(); + evaluated_position_cache_.resize(eval_num); /* TODO: Avoid copying the evaluated data from the temporary array. */ VArray<float3> evaluated = Spline::interpolate_to_evaluated(positions_.as_span()); diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index 122f7f6c059..c3cc268c81c 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -76,7 +76,7 @@ void PolySpline::mark_cache_invalid() length_cache_dirty_ = true; } -int PolySpline::evaluated_points_size() const +int PolySpline::evaluated_points_num() const { return this->size(); } diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c index ee1976d5946..9098c010747 100644 --- a/source/blender/blenkernel/intern/subdiv.c +++ b/source/blender/blenkernel/intern/subdiv.c @@ -25,7 +25,9 @@ #include "opensubdiv_evaluator_capi.h" #include "opensubdiv_topology_refiner_capi.h" -/* =================----====--===== MODULE ==========================------== */ +/* -------------------------------------------------------------------- + * Module. + */ void BKE_subdiv_init() { @@ -37,7 +39,9 @@ void BKE_subdiv_exit() openSubdiv_cleanup(); } -/* ========================== CONVERSION HELPERS ============================ */ +/* -------------------------------------------------------------------- + * Conversion helpers. + */ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int uv_smooth) { @@ -72,7 +76,9 @@ eSubdivVtxBoundaryInterpolation BKE_subdiv_vtx_boundary_interpolation_from_subsu return SUBDIV_VTX_BOUNDARY_EDGE_ONLY; } -/* ================================ SETTINGS ================================ */ +/* -------------------------------------------------------------------- + * Settings. + */ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSettings *settings_b) { @@ -83,7 +89,9 @@ bool BKE_subdiv_settings_equal(const SubdivSettings *settings_a, const SubdivSet settings_a->fvar_linear_interpolation == settings_b->fvar_linear_interpolation); } -/* ============================== CONSTRUCTION ============================== */ +/* -------------------------------------------------------------------- + * Construction. + */ /* Creation from scratch. */ @@ -194,7 +202,9 @@ void BKE_subdiv_free(Subdiv *subdiv) MEM_freeN(subdiv); } -/* =========================== PTEX FACES AND GRIDS ========================= */ +/* -------------------------------------------------------------------- + * Topology helpers. + */ int *BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv) { diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.c b/source/blender/blenkernel/intern/subdiv_ccg_mask.c index 83f76f85a67..04a274d0215 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_mask.c +++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.c @@ -155,8 +155,7 @@ static void mask_init_functions(SubdivCCGMaskEvaluator *mask_evaluator) bool BKE_subdiv_ccg_mask_init_from_paint(SubdivCCGMaskEvaluator *mask_evaluator, const struct Mesh *mesh) { - GridPaintMask *grid_paint_mask = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); - if (grid_paint_mask == NULL) { + if (CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK)) { return false; } /* Allocate all required memory. */ diff --git a/source/blender/blenkernel/intern/subdiv_ccg_material.c b/source/blender/blenkernel/intern/subdiv_ccg_material.c index 9fbc99cb4f1..cf49db15b7b 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_material.c +++ b/source/blender/blenkernel/intern/subdiv_ccg_material.c @@ -10,6 +10,7 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" typedef struct CCGMaterialFromMeshData { const Mesh *mesh; diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index 1c5078df1f3..12a5f00a68b 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -295,16 +295,16 @@ static void init_functions(OpenSubdiv_Converter *converter) static void initialize_manifold_index_array(const BLI_bitmap *used_map, const int num_elements, - int **indices_r, - int **indices_reverse_r, - int *num_manifold_elements_r) + int **r_indices, + int **r_indices_reverse, + int *r_num_manifold_elements) { int *indices = NULL; - if (indices_r != NULL) { + if (r_indices != NULL) { indices = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices"); } int *indices_reverse = NULL; - if (indices_reverse_r != NULL) { + if (r_indices_reverse != NULL) { indices_reverse = MEM_malloc_arrayN(num_elements, sizeof(int), "manifold indices reverse"); } int offset = 0; @@ -324,13 +324,13 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map, offset++; } } - if (indices_r != NULL) { - *indices_r = indices; + if (r_indices != NULL) { + *r_indices = indices; } - if (indices_reverse_r != NULL) { - *indices_reverse_r = indices_reverse; + if (r_indices_reverse != NULL) { + *r_indices_reverse = indices_reverse; } - *num_manifold_elements_r = num_elements - offset; + *r_num_manifold_elements = num_elements - offset; } static void initialize_manifold_indices(ConverterStorage *storage) diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index b8f6dc322c5..1996273b681 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -23,7 +23,9 @@ #include "opensubdiv_evaluator_capi.h" #include "opensubdiv_topology_refiner_capi.h" -/* ============================ Helper Function ============================ */ +/* -------------------------------------------------------------------- + * Helper functions. + */ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( eSubdivEvaluatorType evaluator_type) @@ -40,7 +42,9 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( return OPENSUBDIV_EVALUATOR_CPU; } -/* ====================== Main Subdivision Evaluation ====================== */ +/* -------------------------------------------------------------------- + * Main subdivision evaluation. + */ bool BKE_subdiv_eval_begin(Subdiv *subdiv, eSubdivEvaluatorType evaluator_type, @@ -269,7 +273,9 @@ void BKE_subdiv_eval_init_displacement(Subdiv *subdiv) subdiv->displacement_evaluator->initialize(subdiv->displacement_evaluator); } -/* ========================== Single point queries ========================== */ +/* -------------------------------------------------------------------- + * Single point queries. + */ void BKE_subdiv_eval_limit_point( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3]) diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 5e7a0fc116b..ba2df362b92 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -259,7 +259,10 @@ static void get_face_uv_map_vert( } } -static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, MLoopUV *mloopuv) +static int ss_sync_from_uv(CCGSubSurf *ss, + CCGSubSurf *origss, + DerivedMesh *dm, + const MLoopUV *mloopuv) { MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); @@ -381,13 +384,9 @@ static int ss_sync_from_uv(CCGSubSurf *ss, CCGSubSurf *origss, DerivedMesh *dm, static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result, int n) { - CCGSubSurf *uvss; - CCGFace **faceMap; - MTFace *tf; - MLoopUV *mluv; CCGFaceIterator fi; int index, gridSize, gridFaces, /*edgeSize,*/ totface, x, y, S; - MLoopUV *dmloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, n); + const MLoopUV *dmloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, n); /* need to update both CD_MTFACE & CD_MLOOPUV, hrmf, we could get away with * just tface except applying the modifier then looses subsurf UV */ MTFace *tface = CustomData_get_layer_n(&result->faceData, CD_MTFACE, n); @@ -398,7 +397,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * } /* create a CCGSubSurf from uv's */ - uvss = _getSubSurf(NULL, ccgSubSurf_getSubdivisionLevels(ss), 2, CCG_USE_ARENA); + CCGSubSurf *uvss = _getSubSurf(NULL, ccgSubSurf_getSubdivisionLevels(ss), 2, CCG_USE_ARENA); if (!ss_sync_from_uv(uvss, ss, dm, dmloopuv)) { ccgSubSurf_free(uvss); @@ -412,7 +411,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * gridFaces = gridSize - 1; /* make a map from original faces to CCGFaces */ - faceMap = MEM_mallocN(totface * sizeof(*faceMap), "facemapuv"); + CCGFace **faceMap = MEM_mallocN(totface * sizeof(*faceMap), "facemapuv"); for (ccgSubSurf_initFaceIterator(uvss, &fi); !ccgFaceIterator_isStopped(&fi); ccgFaceIterator_next(&fi)) { CCGFace *f = ccgFaceIterator_getCurrent(&fi); @@ -420,8 +419,8 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * } /* load coordinates from uvss into tface */ - tf = tface; - mluv = mloopuv; + MTFace *tf = tface; + MLoopUV *mluv = mloopuv; for (index = 0; index < totface; index++) { CCGFace *f = faceMap[index]; @@ -568,11 +567,8 @@ static void ss_sync_ccg_from_derivedmesh(CCGSubSurf *ss, MEdge *me; MLoop *mloop = dm->getLoopArray(dm), *ml; MPoly *mpoly = dm->getPolyArray(dm), *mp; - // MFace *mf; /* UNUSED */ int totvert = dm->getNumVerts(dm); int totedge = dm->getNumEdges(dm); - // int totface = dm->getNumTessFaces(dm); /* UNUSED */ - // int totpoly = dm->getNumFaces(dm); /* UNUSED */ int i, j; int *index; @@ -773,11 +769,6 @@ static int ccgDM_getNumPolys(DerivedMesh *dm) return ccgSubSurf_getNumFinalFaces(ccgdm->ss); } -static int ccgDM_getNumTessFaces(DerivedMesh *dm) -{ - return dm->numTessFaceData; -} - static int ccgDM_getNumLoops(DerivedMesh *dm) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; @@ -883,86 +874,6 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) copy_v3_v3(r_no, CCG_elem_no(&key, vd)); } -void subsurf_copy_grid_hidden(DerivedMesh *dm, - const MPoly *mpoly, - MVert *mvert, - const MDisps *mdisps) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int level = ccgSubSurf_getSubdivisionLevels(ss); - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int totface = ccgSubSurf_getNumFaces(ss); - int i, j, x, y; - - for (i = 0; i < totface; i++) { - CCGFace *f = ccgdm->faceMap[i].face; - - for (j = 0; j < mpoly[i].totloop; j++) { - const MDisps *md = &mdisps[mpoly[i].loopstart + j]; - int hidden_gridsize = BKE_ccg_gridsize(md->level); - int factor = BKE_ccg_factor(level, md->level); - BLI_bitmap *hidden = md->hidden; - - if (!hidden) { - continue; - } - - for (y = 0; y < gridSize; y++) { - for (x = 0; x < gridSize; x++) { - int vndx, offset; - - vndx = getFaceIndex(ss, f, j, x, y, edgeSize, gridSize); - offset = (y * factor) * hidden_gridsize + (x * factor); - if (BLI_BITMAP_TEST(hidden, offset)) { - mvert[vndx].flag |= ME_HIDE; - } - } - } - } - } -} - -void subsurf_copy_grid_paint_mask(DerivedMesh *dm, - const MPoly *mpoly, - float *paint_mask, - const GridPaintMask *grid_paint_mask) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int level = ccgSubSurf_getSubdivisionLevels(ss); - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int totface = ccgSubSurf_getNumFaces(ss); - int i, j, x, y, factor, gpm_gridsize; - - for (i = 0; i < totface; i++) { - CCGFace *f = ccgdm->faceMap[i].face; - const MPoly *p = &mpoly[i]; - - for (j = 0; j < p->totloop; j++) { - const GridPaintMask *gpm = &grid_paint_mask[p->loopstart + j]; - if (!gpm->data) { - continue; - } - - factor = BKE_ccg_factor(level, gpm->level); - gpm_gridsize = BKE_ccg_gridsize(gpm->level); - - for (y = 0; y < gridSize; y++) { - for (x = 0; x < gridSize; x++) { - int vndx, offset; - - vndx = getFaceIndex(ss, f, j, x, y, edgeSize, gridSize); - offset = y * factor * gpm_gridsize + x * factor; - paint_mask[vndx] = gpm->data[offset]; - } - } - } - } -} - /* utility function */ BLI_INLINE void ccgDM_to_MVert(MVert *mv, const CCGKey *key, CCGElem *elem) { @@ -1336,8 +1247,9 @@ static void *ccgDM_get_vert_data_layer(DerivedMesh *dm, int type) } BLI_rw_mutex_lock(&ccgdm->origindex_cache_rwlock, THREAD_LOCK_WRITE); - DM_add_vert_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_vert_data_layer(dm, CD_ORIGINDEX); + + origindex = CustomData_add_layer( + &dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numVertData); totorig = ccgSubSurf_getNumVerts(ss); totnone = dm->numVertData - totorig; @@ -1375,8 +1287,8 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type) return origindex; } - DM_add_edge_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_edge_data_layer(dm, CD_ORIGINDEX); + origindex = CustomData_add_layer( + &dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numEdgeData); totedge = ccgSubSurf_getNumEdges(ss); totorig = totedge * (edgeSize - 1); @@ -1418,8 +1330,8 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type) return origindex; } - DM_add_poly_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); - origindex = DM_get_poly_data_layer(dm, CD_ORIGINDEX); + origindex = CustomData_add_layer( + &dm->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numPolyData); totface = ccgSubSurf_getNumFaces(ss); @@ -1592,10 +1504,7 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getNumVerts = ccgDM_getNumVerts; ccgdm->dm.getNumEdges = ccgDM_getNumEdges; ccgdm->dm.getNumLoops = ccgDM_getNumLoops; - /* reuse of ccgDM_getNumTessFaces is intentional here: - * subsurf polys are just created from tessfaces */ ccgdm->dm.getNumPolys = ccgDM_getNumPolys; - ccgdm->dm.getNumTessFaces = ccgDM_getNumTessFaces; ccgdm->dm.getVertCo = ccgDM_getFinalVertCo; ccgdm->dm.getVertNo = ccgDM_getFinalVertNo; @@ -1670,7 +1579,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int index; int i; int vertNum = 0, edgeNum = 0, faceNum = 0; - int *vertOrigIndex, *polyOrigIndex, *base_polyOrigIndex, *edgeOrigIndex; short *edgeFlags = ccgdm->edgeFlags; DMFlagMat *faceFlags = ccgdm->faceFlags; int *polyidx = NULL; @@ -1687,7 +1595,6 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, int gridInternalEdges; WeightTable wtable = {NULL}; MEdge *medge = NULL; - MPoly *mpoly = NULL; bool has_edge_cd; edgeSize = ccgSubSurf_getEdgeSize(ss); @@ -1700,13 +1607,13 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, medge = dm->getEdgeArray(dm); - mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); - base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); + const MPoly *mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); + const int *base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); - vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); - edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *edgeOrigIndex = DM_get_edge_data_layer(&ccgdm->dm, CD_ORIGINDEX); - polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); + int *polyOrigIndex = DM_get_poly_data_layer(&ccgdm->dm, CD_ORIGINDEX); has_edge_cd = ((ccgdm->dm.edgeData.totlayer - (edgeOrigIndex ? 1 : 0)) != 0); @@ -2160,13 +2067,3 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*r_positions)[3]) dm->release(dm); } - -bool subsurf_has_edges(DerivedMesh *dm) -{ - return dm->getNumEdges(dm) != 0; -} - -bool subsurf_has_faces(DerivedMesh *dm) -{ - return dm->getNumPolys(dm) != 0; -} diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 348d6a91eb8..f9d3a44e5cb 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -60,7 +60,9 @@ static struct { ListBase tracks; } tracking_clipboard; -/*********************** Common functions *************************/ +/* -------------------------------------------------------------------- + * Common functions. + */ /* Free the whole list of tracks, list's head and tail are set to NULL. */ static void tracking_tracks_free(ListBase *tracks) @@ -435,7 +437,9 @@ void BKE_tracking_get_projection_matrix(MovieTracking *tracking, } } -/*********************** clipboard *************************/ +/* -------------------------------------------------------------------- + * Clipboard. + */ void BKE_tracking_clipboard_free(void) { @@ -496,7 +500,9 @@ void BKE_tracking_clipboard_paste_tracks(MovieTracking *tracking, MovieTrackingO } } -/*********************** Tracks *************************/ +/* -------------------------------------------------------------------- + * Tracks. + */ MovieTrackingTrack *BKE_tracking_track_add_empty(MovieTracking *tracking, ListBase *tracks_list) { @@ -721,67 +727,76 @@ bool BKE_tracking_track_has_enabled_marker_at_frame(MovieTrackingTrack *track, i return marker && (marker->flag & MARKER_DISABLED) == 0; } -void BKE_tracking_track_path_clear(MovieTrackingTrack *track, int ref_frame, int action) +static void path_clear_remained(MovieTrackingTrack *track, const int ref_frame) { - int a; - - if (action == TRACK_CLEAR_REMAINED) { - a = 1; - - while (a < track->markersnr) { - if (track->markers[a].framenr > ref_frame) { - track->markersnr = a; - track->markers = MEM_reallocN(track->markers, - sizeof(MovieTrackingMarker) * track->markersnr); - - break; - } - - a++; - } + for (int a = 1; a < track->markersnr; a++) { + if (track->markers[a].framenr > ref_frame) { + track->markersnr = a; + track->markers = MEM_reallocN(track->markers, + sizeof(MovieTrackingMarker) * track->markersnr); - if (track->markersnr) { - tracking_marker_insert_disabled(track, &track->markers[track->markersnr - 1], false, true); + break; } } - else if (action == TRACK_CLEAR_UPTO) { - a = track->markersnr - 1; - while (a >= 0) { - if (track->markers[a].framenr <= ref_frame) { - memmove(track->markers, - track->markers + a, - (track->markersnr - a) * sizeof(MovieTrackingMarker)); + if (track->markersnr) { + tracking_marker_insert_disabled(track, &track->markers[track->markersnr - 1], false, true); + } +} - track->markersnr = track->markersnr - a; - track->markers = MEM_reallocN(track->markers, - sizeof(MovieTrackingMarker) * track->markersnr); +static void path_clear_up_to(MovieTrackingTrack *track, const int ref_frame) +{ + for (int a = track->markersnr - 1; a >= 0; a--) { + if (track->markers[a].framenr <= ref_frame) { + memmove(track->markers, + track->markers + a, + (track->markersnr - a) * sizeof(MovieTrackingMarker)); - break; - } + track->markersnr = track->markersnr - a; + track->markers = MEM_reallocN(track->markers, + sizeof(MovieTrackingMarker) * track->markersnr); - a--; + break; } + } - if (track->markersnr) { - tracking_marker_insert_disabled(track, &track->markers[0], true, true); - } + if (track->markersnr) { + tracking_marker_insert_disabled(track, &track->markers[0], true, true); } - else if (action == TRACK_CLEAR_ALL) { - MovieTrackingMarker *marker, marker_new; +} - marker = BKE_tracking_marker_get(track, ref_frame); - marker_new = *marker; +static void path_clear_all(MovieTrackingTrack *track, const int ref_frame) +{ + MovieTrackingMarker *marker, marker_new; - MEM_freeN(track->markers); - track->markers = NULL; - track->markersnr = 0; + marker = BKE_tracking_marker_get(track, ref_frame); + marker_new = *marker; - BKE_tracking_marker_insert(track, &marker_new); + MEM_freeN(track->markers); + track->markers = NULL; + track->markersnr = 0; - tracking_marker_insert_disabled(track, &marker_new, true, true); - tracking_marker_insert_disabled(track, &marker_new, false, true); - } + BKE_tracking_marker_insert(track, &marker_new); + + tracking_marker_insert_disabled(track, &marker_new, true, true); + tracking_marker_insert_disabled(track, &marker_new, false, true); +} + +void BKE_tracking_track_path_clear(MovieTrackingTrack *track, + const int ref_frame, + const eTrackClearAction action) +{ + switch (action) { + case TRACK_CLEAR_REMAINED: + path_clear_remained(track, ref_frame); + break; + case TRACK_CLEAR_UPTO: + path_clear_up_to(track, ref_frame); + break; + case TRACK_CLEAR_ALL: + path_clear_all(track, ref_frame); + break; + }; } void BKE_tracking_tracks_join(MovieTracking *tracking, @@ -1276,7 +1291,9 @@ void BKE_tracking_tracks_deselect_all(ListBase *tracksbase) } } -/*********************** Marker *************************/ +/* -------------------------------------------------------------------- + * Marker. + */ MovieTrackingMarker *BKE_tracking_marker_insert(MovieTrackingTrack *track, MovieTrackingMarker *marker) @@ -1350,60 +1367,52 @@ void BKE_tracking_marker_delete(MovieTrackingTrack *track, int framenr) } } -void BKE_tracking_marker_clamp(MovieTrackingMarker *marker, int event) +void BKE_tracking_marker_clamp_pattern_position(MovieTrackingMarker *marker) { float pat_min[2], pat_max[2]; - BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); - if (event == CLAMP_PAT_DIM) { - for (int a = 0; a < 2; a++) { - /* search shouldn't be resized smaller than pattern */ - marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); - marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); - } - } - else if (event == CLAMP_PAT_POS) { - float dim[2]; - - sub_v2_v2v2(dim, pat_max, pat_min); - - for (int a = 0; a < 2; a++) { - /* pattern shouldn't be moved outside of search */ - if (pat_min[a] < marker->search_min[a]) { - for (int b = 0; b < 4; b++) { - marker->pattern_corners[b][a] += marker->search_min[a] - pat_min[a]; - } + for (int a = 0; a < 2; a++) { + if (pat_min[a] < marker->search_min[a]) { + for (int b = 0; b < 4; b++) { + marker->pattern_corners[b][a] += marker->search_min[a] - pat_min[a]; } - if (pat_max[a] > marker->search_max[a]) { - for (int b = 0; b < 4; b++) { - marker->pattern_corners[b][a] -= pat_max[a] - marker->search_max[a]; - } + } + if (pat_max[a] > marker->search_max[a]) { + for (int b = 0; b < 4; b++) { + marker->pattern_corners[b][a] -= pat_max[a] - marker->search_max[a]; } } } - else if (event == CLAMP_SEARCH_DIM) { - for (int a = 0; a < 2; a++) { - /* search shouldn't be resized smaller than pattern */ - marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); - marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); - } +} + +void BKE_tracking_marker_clamp_search_size(MovieTrackingMarker *marker) +{ + float pat_min[2], pat_max[2]; + BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); + + for (int a = 0; a < 2; a++) { + marker->search_min[a] = min_ff(pat_min[a], marker->search_min[a]); + marker->search_max[a] = max_ff(pat_max[a], marker->search_max[a]); } - else if (event == CLAMP_SEARCH_POS) { - float dim[2]; +} - sub_v2_v2v2(dim, marker->search_max, marker->search_min); +void BKE_tracking_marker_clamp_search_position(MovieTrackingMarker *marker) +{ + float pat_min[2], pat_max[2]; + BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); - for (int a = 0; a < 2; a++) { - /* search shouldn't be moved inside pattern */ - if (marker->search_min[a] > pat_min[a]) { - marker->search_min[a] = pat_min[a]; - marker->search_max[a] = marker->search_min[a] + dim[a]; - } - if (marker->search_max[a] < pat_max[a]) { - marker->search_max[a] = pat_max[a]; - marker->search_min[a] = marker->search_max[a] - dim[a]; - } + float dim[2]; + sub_v2_v2v2(dim, marker->search_max, marker->search_min); + + for (int a = 0; a < 2; a++) { + if (marker->search_min[a] > pat_min[a]) { + marker->search_min[a] = pat_min[a]; + marker->search_max[a] = marker->search_min[a] + dim[a]; + } + if (marker->search_max[a] < pat_max[a]) { + marker->search_max[a] = pat_max[a]; + marker->search_min[a] = marker->search_max[a] - dim[a]; } } } @@ -1591,7 +1600,9 @@ void BKE_tracking_marker_get_subframe_position(MovieTrackingTrack *track, add_v2_v2(pos, track->offset); } -/*********************** Plane Track *************************/ +/* -------------------------------------------------------------------- + * Plane track. + */ MovieTrackingPlaneTrack *BKE_tracking_plane_track_add(MovieTracking *tracking, ListBase *plane_tracks_base, @@ -1796,7 +1807,9 @@ void BKE_tracking_plane_tracks_replace_point_track(MovieTracking *tracking, } } -/*********************** Plane Marker *************************/ +/* -------------------------------------------------------------------- + * Plane marker. + */ MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert(MovieTrackingPlaneTrack *plane_track, MovieTrackingPlaneMarker *plane_marker) @@ -1974,7 +1987,9 @@ void BKE_tracking_plane_marker_get_subframe_corners(MovieTrackingPlaneTrack *pla } } -/*********************** Object *************************/ +/* -------------------------------------------------------------------- + * Object. + */ MovieTrackingObject *BKE_tracking_object_add(MovieTracking *tracking, const char *name) { @@ -2119,7 +2134,9 @@ MovieTrackingReconstruction *BKE_tracking_object_get_reconstruction(MovieTrackin return &object->reconstruction; } -/*********************** Camera *************************/ +/* -------------------------------------------------------------------- + * Camera. + */ static int reconstructed_camera_index_get(MovieTrackingReconstruction *reconstruction, int framenr, @@ -2275,7 +2292,9 @@ void BKE_tracking_camera_get_reconstructed_interpolate(MovieTracking *tracking, reconstructed_camera_scale_set(object, mat); } -/*********************** Distortion/Undistortion *************************/ +/* -------------------------------------------------------------------- + * (Un)distortion. + */ MovieDistortion *BKE_tracking_distortion_new(MovieTracking *tracking, int calibration_width, @@ -2588,7 +2607,9 @@ void BKE_tracking_max_distortion_delta_across_bound(MovieTracking *tracking, } } -/*********************** Image sampling *************************/ +/* -------------------------------------------------------------------- + * Image sampling. + */ static void disable_imbuf_channels(ImBuf *ibuf, MovieTrackingTrack *track, bool grayscale) { @@ -2832,7 +2853,9 @@ void BKE_tracking_disable_channels( } } -/*********************** Dopesheet functions *************************/ +/* -------------------------------------------------------------------- + * Dopesheet functions. + */ /* ** Channels sort comparators ** */ diff --git a/source/blender/blenkernel/intern/tracking_plane_tracker.c b/source/blender/blenkernel/intern/tracking_plane_tracker.c index 5e60f6f59a9..c4379ea61bc 100644 --- a/source/blender/blenkernel/intern/tracking_plane_tracker.c +++ b/source/blender/blenkernel/intern/tracking_plane_tracker.c @@ -21,12 +21,12 @@ typedef double Vec2[2]; static int point_markers_correspondences_on_both_image( - MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **x1_r, Vec2 **x2_r) + MovieTrackingPlaneTrack *plane_track, int frame1, int frame2, Vec2 **r_x1, Vec2 **r_x2) { Vec2 *x1, *x2; - *x1_r = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); - *x2_r = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); + *r_x1 = x1 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x1"); + *r_x2 = x2 = MEM_mallocN(sizeof(*x1) * plane_track->point_tracksnr, "point correspondences x2"); int correspondence_index = 0; for (int i = 0; i < plane_track->point_tracksnr; i++) { diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 02134623a31..30e02e5411b 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -268,7 +268,7 @@ static struct bUnitCollection buImperialAclCollection = {buImperialAclDef, 0, 0, /* Time. */ static struct bUnitDef buNaturalTimeDef[] = { /* Weeks? - probably not needed for Blender. */ - {"day", "days", "d", NULL, "Days", "DAYS", 90000.0, 0.0, B_UNIT_DEF_NONE}, + {"day", "days", "d", NULL, "Days", "DAYS", 86400.0, 0.0, B_UNIT_DEF_NONE}, {"hour", "hours", "hr", "h", "Hours", "HOURS", 3600.0, 0.0, B_UNIT_DEF_NONE}, {"minute", "minutes", "min", "m", "Minutes", "MINUTES", 60.0, 0.0, B_UNIT_DEF_NONE}, {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 307466d7dc9..82405830437 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -97,12 +97,7 @@ static struct VolumeFileCache { /* Cache Entry */ struct Entry { Entry(const std::string &filepath, const openvdb::GridBase::Ptr &grid) - : filepath(filepath), - grid_name(grid->getName()), - grid(grid), - is_loaded(false), - num_metadata_users(0), - num_tree_users(0) + : filepath(filepath), grid_name(grid->getName()), grid(grid) { } @@ -110,9 +105,7 @@ static struct VolumeFileCache { : filepath(other.filepath), grid_name(other.grid_name), grid(other.grid), - is_loaded(other.is_loaded), - num_metadata_users(0), - num_tree_users(0) + is_loaded(other.is_loaded) { } @@ -151,12 +144,12 @@ static struct VolumeFileCache { blender::Map<int, openvdb::GridBase::Ptr> simplified_grids; /* Has the grid tree been loaded? */ - mutable bool is_loaded; + mutable bool is_loaded = false; /* Error message if an error occurred while loading. */ std::string error_msg; /* User counting. */ - int num_metadata_users; - int num_tree_users; + int num_metadata_users = 0; + int num_tree_users = 0; /* Mutex for on-demand reading of tree. */ mutable std::mutex mutex; }; diff --git a/source/blender/blenlib/BLI_any.hh b/source/blender/blenlib/BLI_any.hh index ca3d5756c52..e80dad82d01 100644 --- a/source/blender/blenlib/BLI_any.hh +++ b/source/blender/blenlib/BLI_any.hh @@ -184,7 +184,7 @@ class Any { } /** - * \note: Only needed because the template below does not count as copy assignment operator. + * \note Only needed because the template below does not count as copy assignment operator. */ Any &operator=(const Any &other) { diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 91dfc81ae27..813277d9968 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -64,10 +64,10 @@ class Array { int64_t size_; /** Used for allocations when the inline buffer is too small. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; public: /** diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h index ca11cd6574e..19d8525311c 100644 --- a/source/blender/blenlib/BLI_bitmap.h +++ b/source/blender/blenlib/BLI_bitmap.h @@ -15,7 +15,7 @@ extern "C" { typedef unsigned int BLI_bitmap; -/* warning: the bitmap does not keep track of its own size or check +/* WARNING: the bitmap does not keep track of its own size or check * for out-of-bounds access */ /* internal use */ diff --git a/source/blender/blenlib/BLI_color_mix.hh b/source/blender/blenlib/BLI_color_mix.hh index 4989ddc609e..322da2bf112 100644 --- a/source/blender/blenlib/BLI_color_mix.hh +++ b/source/blender/blenlib/BLI_color_mix.hh @@ -1042,7 +1042,7 @@ BLI_INLINE Color BLI_mix_colors(const IMB_BlendMode tool, case IMB_BLEND_COLOR: return mix_color<Color, Traits>(a, b, alpha); default: - BLI_assert(0); + BLI_assert_unreachable(); return Color(0, 0, 0, 0); } } diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 3ce2b90e729..063e60ecf03 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -178,7 +178,7 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries); * Convert given entry's size into human-readable strings. */ void BLI_filelist_entry_size_to_string(const struct stat *st, - uint64_t sz, + uint64_t st_size_fallback, bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN]); /** @@ -215,10 +215,10 @@ void BLI_filelist_entry_datetime_to_string(const struct stat *st, /** \name Files * \{ */ -FILE *BLI_fopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -void *BLI_gzopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BLI_open(const char *filename, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +FILE *BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void *BLI_gzopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /** * Returns true if the file with the specified name can be written. @@ -226,7 +226,7 @@ int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONN * to the real UID and GID of the process, not its effective UID and GID. * This shouldn't matter for Blender, which is not going to run privileged anyway. */ -bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_is_writable(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /** * Creates the file with nothing in it, or updates its last-modified date if it already exists. * Returns true if successful (like the unix touch command). @@ -319,7 +319,7 @@ const char *BLI_expand_tilde(const char *path_with_tilde); # define O_BINARY 0 # endif #else -void BLI_get_short_name(char short_name[256], const char *filename); +void BLI_get_short_name(char short_name[256], const char *filepath); #endif /** \} */ diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh new file mode 100644 index 00000000000..62478556d9b --- /dev/null +++ b/source/blender/blenlib/BLI_float3x3.hh @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cmath> +#include <cstdint> + +#include "BLI_assert.h" +#include "BLI_math_base.h" +#include "BLI_math_matrix.h" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.h" + +namespace blender { + +struct float3x3 { + /* A 3x3 matrix in column major order. */ + float values[3][3]; + + float3x3() = default; + + float3x3(const float *matrix) + { + memcpy(values, matrix, sizeof(float) * 3 * 3); + } + + float3x3(const float matrix[3][3]) : float3x3(static_cast<const float *>(matrix[0])) + { + } + + static float3x3 zero() + { + float3x3 result; + zero_m3(result.values); + return result; + } + + static float3x3 identity() + { + float3x3 result; + unit_m3(result.values); + return result; + } + + static float3x3 from_translation(const float2 translation) + { + float3x3 result = identity(); + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + return result; + } + + static float3x3 from_rotation(float rotation) + { + float3x3 result = zero(); + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = cosine; + result.values[0][1] = sine; + result.values[1][0] = -sine; + result.values[1][1] = cosine; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_translation_rotation_scale(const float2 translation, + float rotation, + const float2 scale) + { + float3x3 result; + const float cosine = std::cos(rotation); + const float sine = std::sin(rotation); + result.values[0][0] = scale.x * cosine; + result.values[0][1] = scale.x * sine; + result.values[0][2] = 0.0f; + result.values[1][0] = scale.y * -sine; + result.values[1][1] = scale.y * cosine; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + static float3x3 from_normalized_axes(const float2 translation, + const float2 horizontal, + const float2 vertical) + { + BLI_ASSERT_UNIT_V2(horizontal); + BLI_ASSERT_UNIT_V2(vertical); + + float3x3 result; + result.values[0][0] = horizontal.x; + result.values[0][1] = horizontal.y; + result.values[0][2] = 0.0f; + result.values[1][0] = vertical.x; + result.values[1][1] = vertical.y; + result.values[1][2] = 0.0f; + result.values[2][0] = translation.x; + result.values[2][1] = translation.y; + result.values[2][2] = 1.0f; + return result; + } + + /* Construct a transformation that is pivoted around the given origin point. So for instance, + * from_origin_transformation(from_rotation(M_PI_2), float2(0.0f, 2.0f)) + * will construct a transformation representing a 90 degree rotation around the point (0, 2). */ + static float3x3 from_origin_transformation(const float3x3 &transformation, const float2 origin) + { + return from_translation(origin) * transformation * from_translation(-origin); + } + + operator float *() + { + return &values[0][0]; + } + + operator const float *() const + { + return &values[0][0]; + } + + float *operator[](const int64_t index) + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + const float *operator[](const int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < 3); + return &values[index][0]; + } + + using c_style_float3x3 = float[3][3]; + c_style_float3x3 &ptr() + { + return values; + } + + const c_style_float3x3 &ptr() const + { + return values; + } + + friend float3x3 operator*(const float3x3 &a, const float3x3 &b) + { + float3x3 result; + mul_m3_m3m3(result.values, a.values, b.values); + return result; + } + + void operator*=(const float3x3 &other) + { + mul_m3_m3_post(values, other.values); + } + + friend float2 operator*(const float3x3 &transformation, const float2 &vector) + { + float2 result; + mul_v2_m3v2(result, transformation.values, vector); + return result; + } + + friend float2 operator*(const float3x3 &transformation, const float (*vector)[2]) + { + return transformation * float2(vector); + } + + float3x3 transposed() const + { + float3x3 result; + transpose_m3_m3(result.values, values); + return result; + } + + float3x3 inverted() const + { + float3x3 result; + invert_m3_m3(result.values, values); + return result; + } + + friend bool operator==(const float3x3 &a, const float3x3 &b) + { + return equals_m3m3(a.values, b.values); + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 9f7fbffc692..64e6e68432f 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -147,6 +147,16 @@ struct float4x4 { return m * float3(v); } + friend bool operator==(const float4x4 &a, const float4x4 &b) + { + return equals_m4m4(a.ptr(), b.ptr()); + } + + friend bool operator!=(const float4x4 &a, const float4x4 &b) + { + return !(a == b); + } + float3 translation() const { return float3(values[3]); @@ -246,6 +256,25 @@ struct float4x4 { } return h; } + + friend std::ostream &operator<<(std::ostream &stream, const float4x4 &mat) + { + char fchar[16]; + stream << "(\n"; + for (int i = 0; i < 4; i++) { + stream << "("; + for (int j = 0; j < 4; j++) { + snprintf(fchar, sizeof(fchar), "%11.6f", mat[j][i]); + stream << fchar; + if (i != 3) { + stream << ", "; + } + } + stream << ")\n"; + } + stream << ")\n"; + return stream; + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_generic_array.hh b/source/blender/blenlib/BLI_generic_array.hh index e1b6b29874a..4b917434264 100644 --- a/source/blender/blenlib/BLI_generic_array.hh +++ b/source/blender/blenlib/BLI_generic_array.hh @@ -33,7 +33,7 @@ class GArray { void *data_ = nullptr; int64_t size_ = 0; - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; public: /** diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index d02760d9178..3fce2947d0d 100644 --- a/source/blender/blenlib/BLI_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -61,7 +61,9 @@ class GVArrayImpl { /* A generic version of #VMutableArrayImpl. */ class GVMutableArrayImpl : public GVArrayImpl { public: - GVMutableArrayImpl(const CPPType &type, int64_t size); + GVMutableArrayImpl(const CPPType &type, int64_t size) : GVArrayImpl(type, size) + { + } virtual void set_by_copy(int64_t index, const void *value); virtual void set_by_relocate(int64_t index, void *value); @@ -105,7 +107,7 @@ class GVArrayCommon { Storage storage_; protected: - GVArrayCommon(); + GVArrayCommon() = default; GVArrayCommon(const GVArrayCommon &other); GVArrayCommon(GVArrayCommon &&other) noexcept; GVArrayCommon(const GVArrayImpl *impl); @@ -186,6 +188,10 @@ class GVArray : public GVArrayCommon { GVArray(const GVArrayImpl *impl); GVArray(std::shared_ptr<const GVArrayImpl> impl); + GVArray(varray_tag::span /* tag */, GSpan span); + GVArray(varray_tag::single_ref /* tag */, const CPPType &type, int64_t size, const void *value); + GVArray(varray_tag::single /* tag */, const CPPType &type, int64_t size, const void *value); + template<typename T> GVArray(const VArray<T> &varray); template<typename T> VArray<T> typed() const; @@ -643,10 +649,18 @@ class GVArrayImpl_For_GSpan : public GVMutableArrayImpl { const int64_t element_size_; public: - GVArrayImpl_For_GSpan(const GMutableSpan span); + GVArrayImpl_For_GSpan(const GMutableSpan span) + : GVMutableArrayImpl(span.type(), span.size()), + data_(span.data()), + element_size_(span.type().size()) + { + } protected: - GVArrayImpl_For_GSpan(const CPPType &type, int64_t size); + GVArrayImpl_For_GSpan(const CPPType &type, int64_t size) + : GVMutableArrayImpl(type, size), element_size_(type.size()) + { + } public: void get(int64_t index, void *r_value) const override; @@ -667,6 +681,61 @@ class GVArrayImpl_For_GSpan : public GVMutableArrayImpl { void *dst) const override; }; +class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan { + public: + using GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan; + + private: + bool may_have_ownership() const override + { + return false; + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArrayImpl_For_SingleValueRef. + * \{ */ + +class GVArrayImpl_For_SingleValueRef : public GVArrayImpl { + protected: + const void *value_ = nullptr; + + public: + GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) + : GVArrayImpl(type, size), value_(value) + { + } + + protected: + GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArrayImpl(type, size) + { + } + + void get(const int64_t index, void *r_value) const override; + void get_to_uninitialized(const int64_t index, void *r_value) const override; + bool is_span() const override; + GSpan get_internal_span() const override; + bool is_single() const override; + void get_internal_single(void *r_value) const override; + void materialize(const IndexMask mask, void *dst) const override; + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override; + void materialize_compressed(const IndexMask mask, void *dst) const override; + void materialize_compressed_to_uninitialized(const IndexMask mask, void *dst) const override; +}; + +class GVArrayImpl_For_SingleValueRef_final final : public GVArrayImpl_For_SingleValueRef { + public: + using GVArrayImpl_For_SingleValueRef::GVArrayImpl_For_SingleValueRef; + + private: + bool may_have_ownership() const override + { + return false; + } +}; + /** \} */ /* -------------------------------------------------------------------- */ @@ -809,6 +878,22 @@ inline bool GVArrayCommon::is_empty() const /** \name Inline methods for #GVArray. * \{ */ +inline GVArray::GVArray(varray_tag::span /* tag */, const GSpan span) +{ + /* Use const-cast because the underlying virtual array implementation is shared between const + * and non const data. */ + GMutableSpan mutable_span{span.type(), const_cast<void *>(span.data()), span.size()}; + this->emplace<GVArrayImpl_For_GSpan_final>(mutable_span); +} + +inline GVArray::GVArray(varray_tag::single_ref /* tag */, + const CPPType &type, + const int64_t size, + const void *value) +{ + this->emplace<GVArrayImpl_For_SingleValueRef_final>(type, size, value); +} + namespace detail { template<typename StorageT> constexpr GVArrayAnyExtraInfo GVArrayAnyExtraInfo::get() { diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh index 6fe1c6513a2..ec0fabb75b4 100644 --- a/source/blender/blenlib/BLI_length_parameterize.hh +++ b/source/blender/blenlib/BLI_length_parameterize.hh @@ -17,7 +17,7 @@ namespace blender::length_parameterize { * Return the size of the necessary lengths array for a group of points, taking into account the * possible last cyclic segment. * - * \note This is the same as #bke::curves::curve_segment_size. + * \note This is the same as #bke::curves::curve_segment_num. */ inline int lengths_num(const int points_num, const bool cyclic) { @@ -84,7 +84,7 @@ void create_uniform_samples(Span<float> lengths, * Could be calculated by #accumulate_lengths. * \param sample_lengths: Sampled locations in the #lengths array. Must be sorted and is expected * to be within the range of the #lengths values. - * \param cyclic: Whether the points described by the #lenghts input is cyclic. This is likely + * \param cyclic: Whether the points described by the #lengths input is cyclic. This is likely * redundant information theoretically. * \param indices: The index of the previous point at each sample. * \param factors: The portion of the length in each segment at each sample. diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh index 6532c59a846..deb6ea3b5fd 100644 --- a/source/blender/blenlib/BLI_linear_allocator.hh +++ b/source/blender/blenlib/BLI_linear_allocator.hh @@ -18,7 +18,7 @@ namespace blender { template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopyable, NonMovable { private: - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; Vector<void *> owned_buffers_; Vector<Span<char>> unused_borrowed_buffers_; diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index d76aa46502d..55233676ed8 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -130,10 +130,10 @@ class Map { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index f9c05fb116e..f072a17f384 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -204,13 +204,17 @@ MINLINE int integer_digits_i(int i); /* These don't really fit anywhere but were being copied about a lot. */ MINLINE int is_power_of_2_i(int n); + +MINLINE unsigned int log2_floor_u(unsigned int x); +MINLINE unsigned int log2_ceil_u(unsigned int x); + +/** + * Returns next (or previous) power of 2 or the input number if it is already a power of 2. + */ MINLINE int power_of_2_max_i(int n); MINLINE int power_of_2_min_i(int n); - MINLINE unsigned int power_of_2_max_u(unsigned int x); MINLINE unsigned int power_of_2_min_u(unsigned int x); -MINLINE unsigned int log2_floor_u(unsigned int x); -MINLINE unsigned int log2_ceil_u(unsigned int x); /** * Integer division that rounds 0.5 up, particularly useful for color blending diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh index 034c6968c94..3057e30dc03 100644 --- a/source/blender/blenlib/BLI_math_base.hh +++ b/source/blender/blenlib/BLI_math_base.hh @@ -14,19 +14,9 @@ #include "BLI_math_base_safe.h" #include "BLI_utildefines.h" -#ifdef WITH_GMP -# include "BLI_math_mpq.hh" -#endif - namespace blender::math { -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_float_type = std::is_floating_point_v<T>; template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>; template<typename T> inline bool is_zero(const T &a) diff --git a/source/blender/blenlib/BLI_math_mpq.hh b/source/blender/blenlib/BLI_math_mpq.hh index 7b43c90da84..02c92705323 100644 --- a/source/blender/blenlib/BLI_math_mpq.hh +++ b/source/blender/blenlib/BLI_math_mpq.hh @@ -19,4 +19,10 @@ */ # include "gmpxx.h" +# include "BLI_math_base.hh" + +namespace blender::math { +template<> inline constexpr bool is_math_float_type<mpq_class> = true; +} + #endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 7d10e52f699..192ad482a69 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -7,6 +7,7 @@ * \ingroup bli */ +#include "BLI_math_base.h" #include "BLI_utildefines.h" #include "DNA_vec_types.h" diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index e36bbedee32..7f20881dfa3 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -14,10 +14,6 @@ #include "BLI_utildefines.h" -#ifdef WITH_GMP -# include "BLI_math_mpq.hh" -#endif - namespace blender { /* clang-format off */ diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index d7c41ae88a8..940542c9f1d 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -317,30 +317,36 @@ template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddr * An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will * not be initialized by the default constructor. */ -template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { - private: - /* Don't create an empty array. This causes problems with some compilers. */ - char buffer_[(Size > 0) ? Size : 1]; +template<size_t Size, size_t Alignment> class AlignedBuffer { + struct Empty { + }; + struct alignas(Alignment) Sized { + /* Don't create an empty array. This causes problems with some compilers. */ + std::byte buffer_[Size > 0 ? Size : 1]; + }; + + using BufferType = std::conditional_t<Size == 0, Empty, Sized>; + BLI_NO_UNIQUE_ADDRESS BufferType buffer_; public: operator void *() { - return buffer_; + return this; } operator const void *() const { - return buffer_; + return this; } void *ptr() { - return buffer_; + return this; } const void *ptr() const { - return buffer_; + return this; } }; @@ -351,7 +357,7 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { */ template<typename T, int64_t Size = 1> class TypedBuffer { private: - AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_; + BLI_NO_UNIQUE_ADDRESS AlignedBuffer<sizeof(T) * (size_t)Size, alignof(T)> buffer_; public: operator T *() diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 391d31c2228..62de4b79e41 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -136,10 +136,10 @@ class Set { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 @@ -483,10 +483,10 @@ class Set { * while iterating over the set. However, after this method has been called, the removed element * must not be accessed anymore. */ - void remove(const Iterator &iterator) + void remove(const Iterator &it) { /* The const cast is valid because this method itself is not const. */ - Slot &slot = const_cast<Slot &>(iterator.current_slot()); + Slot &slot = const_cast<Slot &>(it.current_slot()); BLI_assert(slot.is_occupied()); slot.remove(); removed_slots_++; diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index a06515a7781..ed123f43a6b 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -96,7 +96,7 @@ class Stack { int64_t size_; /** The buffer used to implement small object optimization. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * A chunk referencing the inline buffer. This is always the bottom-most chunk. @@ -105,7 +105,7 @@ class Stack { Chunk inline_chunk_; /** Used for allocations when the inline buffer is not large enough. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; public: /** diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 0344622e81d..15926e8f2d2 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -307,7 +307,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, bool base_10) A * * Length of 7 is the maximum of the resulting string, for example, `-15.5K\0`. */ -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) ATTR_NONNULL(); +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) ATTR_NONNULL(); /** * Compare two strings without regard to case. * diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index d87a86ce696..904dea66f7a 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -77,17 +77,19 @@ Value parallel_reduce(IndexRange range, const Reduction &reduction) { #ifdef WITH_TBB - return tbb::parallel_reduce( - tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), - identity, - [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { - return function(IndexRange(subrange.begin(), subrange.size()), ident); - }, - reduction); + if (range.size() >= grain_size) { + return tbb::parallel_reduce( + tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size), + identity, + [&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) { + return function(IndexRange(subrange.begin(), subrange.size()), ident); + }, + reduction); + } #else UNUSED_VARS(grain_size, reduction); - return function(range, identity); #endif + return function(range, identity); } /** diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index b8407a5453f..7f9470a9111 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -843,6 +843,18 @@ extern bool BLI_memory_is_zero(const void *arr, size_t arr_size); */ #define BLI_ENABLE_IF(condition) typename std::enable_if_t<(condition)> * = nullptr +#if defined(_MSC_VER) +# define BLI_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(no_unique_address) +# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]] +# else +# define BLI_NO_UNIQUE_ADDRESS +# endif +#else +# define BLI_NO_UNIQUE_ADDRESS [[no_unique_address]] +#endif + /** \} */ #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index acf47f67168..c23d846d277 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -84,10 +84,10 @@ class Vector { T *capacity_end_; /** Used for allocations when the inline buffer is too small. */ - Allocator allocator_; + BLI_NO_UNIQUE_ADDRESS Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - TypedBuffer<T, InlineBufferCapacity> inline_buffer_; + BLI_NO_UNIQUE_ADDRESS TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * Store the size of the vector explicitly in debug builds. Otherwise you'd always have to call diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 4ae1bf9000d..b0a3696f245 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -117,10 +117,10 @@ class VectorSet { uint64_t slot_mask_; /** This is called to hash incoming keys. */ - Hash hash_; + BLI_NO_UNIQUE_ADDRESS Hash hash_; /** This is called to check equality of two keys. */ - IsEqual is_equal_; + BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_; /** The max load factor is 1/2 = 50% by default. */ #define LOAD_FACTOR 1, 2 diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 7aa221f62ce..ab4ca185ddb 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -463,7 +463,7 @@ template<typename T, typename GetFunc> class VArrayImpl_For_Func final : public }; /** - * \note: This is `final` so that #may_have_ownership can be implemented reliably. + * \note This is `final` so that #may_have_ownership can be implemented reliably. */ template<typename StructT, typename ElemT, @@ -725,7 +725,7 @@ template<typename T> class VArrayCommon { /** * Get the element at a specific index. - * \note: This can't return a reference because the value may be computed on the fly. This also + * \note This can't return a reference because the value may be computed on the fly. This also * implies that one can not use this method for assignments. */ T operator[](const int64_t index) const @@ -872,6 +872,22 @@ template<typename T> class VArrayCommon { template<typename T> class VMutableArray; /** + * Various tags to disambiguate constructors of virtual arrays. + * Generally it is easier to use `VArray::For*` functions to construct virtual arrays, but + * sometimes being able to use the constructor can result in better performance For example, when + * constructing the virtual array directly in a vector. Without the constructor one would have to + * construct the virtual array first and then move it into the vector. + */ +namespace varray_tag { +struct span { +}; +struct single_ref { +}; +struct single { +}; +} // namespace varray_tag + +/** * A #VArray wraps a virtual array implementation and provides easy access to its elements. It can * be copied and moved. While it is relatively small, it should still be passed by reference if * possible (other than e.g. #Span). @@ -892,6 +908,19 @@ template<typename T> class VArray : public VArrayCommon<T> { { } + VArray(varray_tag::span /* tag */, Span<T> span) + { + /* Cast const away, because the virtual array implementation for const and non const spans is + * shared. */ + MutableSpan<T> mutable_span{const_cast<T *>(span.data()), span.size()}; + this->template emplace<VArrayImpl_For_Span_final<T>>(mutable_span); + } + + VArray(varray_tag::single /* tag */, T value, const int64_t size) + { + this->template emplace<VArrayImpl_For_Single<T>>(std::move(value), size); + } + /** * Construct a new virtual array for a custom #VArrayImpl. */ @@ -908,7 +937,7 @@ template<typename T> class VArray : public VArrayCommon<T> { */ static VArray ForSingle(T value, const int64_t size) { - return VArray::For<VArrayImpl_For_Single<T>>(std::move(value), size); + return VArray(varray_tag::single{}, std::move(value), size); } /** @@ -917,10 +946,7 @@ template<typename T> class VArray : public VArrayCommon<T> { */ static VArray ForSpan(Span<T> values) { - /* Cast const away, because the virtual array implementation for const and non const spans is - * shared. */ - MutableSpan<T> span{const_cast<T *>(values.data()), values.size()}; - return VArray::For<VArrayImpl_For_Span_final<T>>(span); + return VArray(varray_tag::span{}, values); } /** diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e0f28522d6c..109230ebfa7 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -79,8 +79,8 @@ set(SRC intern/kdtree_3d.c intern/kdtree_4d.c intern/lasso_2d.c - intern/listbase.c intern/length_parameterize.cc + intern/listbase.c intern/math_base.c intern/math_base_inline.c intern/math_base_safe_inline.c @@ -199,6 +199,7 @@ set(SRC BLI_fileops.hh BLI_fileops_types.h BLI_filereader.h + BLI_float3x3.hh BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh @@ -274,8 +275,8 @@ set(SRC BLI_multi_value_map.hh BLI_noise.h BLI_noise.hh - BLI_path_util.h BLI_parameter_pack_utils.hh + BLI_path_util.h BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h BLI_probing_strategies.hh @@ -431,6 +432,7 @@ if(WITH_GTESTS) tests/BLI_edgehash_test.cc tests/BLI_expr_pylike_eval_test.cc tests/BLI_fileops_test.cc + tests/BLI_float3x3_test.cc tests/BLI_function_ref_test.cc tests/BLI_generic_array_test.cc tests/BLI_generic_span_test.cc diff --git a/source/blender/blenlib/intern/BLI_assert.c b/source/blender/blenlib/intern/BLI_assert.c index 2ebeba43dbe..96c16b47214 100644 --- a/source/blender/blenlib/intern/BLI_assert.c +++ b/source/blender/blenlib/intern/BLI_assert.c @@ -40,7 +40,7 @@ void _BLI_assert_abort(void) /* Wrap to remove 'noreturn' attribute since this suppresses missing return statements, * allowing changes to debug builds to accidentally to break release builds. * - * For example `BLI_assert(0);` at the end of a function that returns a value, + * For example `BLI_assert_unreachable();` at the end of a function that returns a value, * will hide that it's missing a return. */ abort(); diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c index 76fc5b6342a..c6178ebb3a0 100644 --- a/source/blender/blenlib/intern/BLI_filelist.c +++ b/source/blender/blenlib/intern/BLI_filelist.c @@ -237,7 +237,7 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_ } void BLI_filelist_entry_size_to_string(const struct stat *st, - const uint64_t sz, + const uint64_t st_size_fallback, /* Used to change MB -> M, etc. - is that really useful? */ const bool UNUSED(compact), char r_size[FILELIST_DIRENTRY_SIZE_LEN]) @@ -247,7 +247,7 @@ void BLI_filelist_entry_size_to_string(const struct stat *st, * will buy us some time until files get bigger than 4GB or until * everyone starts using __USE_FILE_OFFSET64 or equivalent. */ - double size = (double)(st ? st->st_size : sz); + double size = (double)(st ? st->st_size : st_size_fallback); #ifdef WIN32 BLI_str_format_byte_unit(r_size, size, false); #else diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 57e05233efa..e6ff5bab8a1 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -173,7 +173,7 @@ BLI_INLINE uint ghash_find_next_bucket_index(const GHash *gh, uint curr_bucket) return curr_bucket; } } - BLI_assert(0); + BLI_assert_unreachable(); return 0; } diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 0f52c84c45e..62bf17bd415 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -893,7 +893,7 @@ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) } else { /* should never happen! */ - BLI_assert(0); + BLI_assert_unreachable(); goto fail; } diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c index f1f1dd60ddf..a401059755d 100644 --- a/source/blender/blenlib/intern/array_utils.c +++ b/source/blender/blenlib/intern/array_utils.c @@ -53,7 +53,7 @@ void _bli_array_wrap(void *arr_v, uint arr_len, size_t arr_stride, int dir) memcpy(arr, buf, arr_stride); } else { - BLI_assert(0); + BLI_assert_unreachable(); } } diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 5ca6fe2efd0..3abd482d6b3 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -164,10 +164,10 @@ bool BLI_file_magic_is_zstd(const char header[4]) return false; } -bool BLI_file_is_writable(const char *filename) +bool BLI_file_is_writable(const char *filepath) { bool writable; - if (BLI_access(filename, W_OK) == 0) { + if (BLI_access(filepath, W_OK) == 0) { /* file exists and I can write to it */ writable = true; } @@ -178,7 +178,7 @@ bool BLI_file_is_writable(const char *filename) else { /* file doesn't exist -- check I can create it in parent directory */ char parent[FILE_MAX]; - BLI_split_dirfile(filename, parent, NULL, sizeof(parent), 0); + BLI_split_dirfile(filepath, parent, NULL, sizeof(parent), 0); #ifdef WIN32 /* windows does not have X_OK */ writable = BLI_access(parent, W_OK) == 0; @@ -224,38 +224,38 @@ static void callLocalErrorCallBack(const char *err) printf("%s\n", err); } -FILE *BLI_fopen(const char *filename, const char *mode) +FILE *BLI_fopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return ufopen(filename, mode); + return ufopen(filepath, mode); } -void BLI_get_short_name(char short_name[256], const char *filename) +void BLI_get_short_name(char short_name[256], const char *filepath) { wchar_t short_name_16[256]; int i = 0; - UTF16_ENCODE(filename); + UTF16_ENCODE(filepath); - GetShortPathNameW(filename_16, short_name_16, 256); + GetShortPathNameW(filepath_16, short_name_16, 256); for (i = 0; i < 256; i++) { short_name[i] = (char)short_name_16[i]; } - UTF16_UN_ENCODE(filename); + UTF16_UN_ENCODE(filepath); } -void *BLI_gzopen(const char *filename, const char *mode) +void *BLI_gzopen(const char *filepath, const char *mode) { gzFile gzfile; - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); /* XXX: Creates file before transcribing the path. */ if (mode[0] == 'w') { - FILE *file = ufopen(filename, "a"); + FILE *file = ufopen(filepath, "a"); if (file == NULL) { /* File couldn't be opened, e.g. due to permission error. */ return NULL; @@ -266,15 +266,15 @@ void *BLI_gzopen(const char *filename, const char *mode) /* temporary #if until we update all libraries to 1.2.7 * for correct wide char path handling */ # if ZLIB_VERNUM >= 0x1270 - UTF16_ENCODE(filename); + UTF16_ENCODE(filepath); - gzfile = gzopen_w(filename_16, mode); + gzfile = gzopen_w(filepath_16, mode); - UTF16_UN_ENCODE(filename); + UTF16_UN_ENCODE(filepath); # else { char short_name[256]; - BLI_get_short_name(short_name, filename); + BLI_get_short_name(short_name, filepath); gzfile = gzopen(short_name, mode); } # endif @@ -282,18 +282,18 @@ void *BLI_gzopen(const char *filename, const char *mode) return gzfile; } -int BLI_open(const char *filename, int oflag, int pmode) +int BLI_open(const char *filepath, int oflag, int pmode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return uopen(filename, oflag, pmode); + return uopen(filepath, oflag, pmode); } -int BLI_access(const char *filename, int mode) +int BLI_access(const char *filepath, int mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return uaccess(filename, mode); + return uaccess(filepath, mode); } static bool delete_soft(const wchar_t *path_16, const char **error_message) @@ -466,8 +466,8 @@ int BLI_move(const char *file, const char *to) int err; /* windows doesn't support moving to a directory - * it has to be 'mv filename filename' and not - * 'mv filename destination_directory' */ + * it has to be 'mv filepath filepath' and not + * 'mv filepath destination_directory' */ BLI_strncpy(str, to, sizeof(str)); /* points 'to' to a directory ? */ @@ -498,8 +498,8 @@ int BLI_copy(const char *file, const char *to) int err; /* windows doesn't support copying to a directory - * it has to be 'cp filename filename' and not - * 'cp filename destdir' */ + * it has to be 'cp filepath filepath' and not + * 'cp filepath destdir' */ BLI_strncpy(str, to, sizeof(str)); /* points 'to' to a directory ? */ @@ -587,7 +587,7 @@ int BLI_rename(const char *from, const char *to) return 0; } - /* make sure the filenames are different (case insensitive) before removing */ + /* Make sure `from` & `to` are different (case insensitive) before removing. */ if (BLI_exists(to) && BLI_strcasecmp(from, to)) { if (BLI_delete(to, false, false)) { return 1; @@ -728,9 +728,9 @@ static int recursive_operation(const char *startfrom, # ifdef __HAIKU__ { struct stat st_dir; - char filename[FILE_MAX]; - BLI_path_join(filename, sizeof(filename), startfrom, dirent->d_name, NULL); - lstat(filename, &st_dir); + char filepath[FILE_MAX]; + BLI_path_join(filepath, sizeof(filepath), startfrom, dirent->d_name, NULL); + lstat(filepath, &st_dir); is_dir = S_ISDIR(st_dir.st_mode); } # else @@ -903,32 +903,32 @@ static int delete_soft(const char *file, const char **error_message) } # endif -FILE *BLI_fopen(const char *filename, const char *mode) +FILE *BLI_fopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return fopen(filename, mode); + return fopen(filepath, mode); } -void *BLI_gzopen(const char *filename, const char *mode) +void *BLI_gzopen(const char *filepath, const char *mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return gzopen(filename, mode); + return gzopen(filepath, mode); } -int BLI_open(const char *filename, int oflag, int pmode) +int BLI_open(const char *filepath, int oflag, int pmode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return open(filename, oflag, pmode); + return open(filepath, oflag, pmode); } -int BLI_access(const char *filename, int mode) +int BLI_access(const char *filepath, int mode) { - BLI_assert(!BLI_path_is_rel(filename)); + BLI_assert(!BLI_path_is_rel(filepath)); - return access(filename, mode); + return access(filepath, mode); } int BLI_delete(const char *file, bool dir, bool recursive) diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc index a3a17952a97..a6fbf4bff5b 100644 --- a/source/blender/blenlib/intern/generic_virtual_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_array.cc @@ -85,11 +85,6 @@ bool GVArrayImpl::may_have_ownership() const /** \name #GVMutableArrayImpl * \{ */ -GVMutableArrayImpl::GVMutableArrayImpl(const CPPType &type, const int64_t size) - : GVArrayImpl(type, size) -{ -} - void GVMutableArrayImpl::set_by_copy(const int64_t index, const void *value) { BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer); @@ -141,18 +136,6 @@ bool GVMutableArrayImpl::try_assign_VMutableArray(void *UNUSED(varray)) const /** \name #GVArrayImpl_For_GSpan * \{ */ -GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan(const GMutableSpan span) - : GVMutableArrayImpl(span.type(), span.size()), - data_(span.data()), - element_size_(span.type().size()) -{ -} - -GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan(const CPPType &type, const int64_t size) - : GVMutableArrayImpl(type, size), element_size_(type.size()) -{ -} - void GVArrayImpl_For_GSpan::get(const int64_t index, void *r_value) const { type_->copy_assign(POINTER_OFFSET(data_, element_size_ * index), r_value); @@ -209,17 +192,6 @@ void GVArrayImpl_For_GSpan::materialize_compressed_to_uninitialized(const IndexM type_->copy_construct_compressed(data_, dst, mask); } -class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan { - public: - using GVArrayImpl_For_GSpan::GVArrayImpl_For_GSpan; - - private: - bool may_have_ownership() const override - { - return false; - } -}; - /** \} */ /* -------------------------------------------------------------------- */ @@ -227,79 +199,56 @@ class GVArrayImpl_For_GSpan_final final : public GVArrayImpl_For_GSpan { * \{ */ /* Generic virtual array where each element has the same value. The value is not owned. */ -class GVArrayImpl_For_SingleValueRef : public GVArrayImpl { - protected: - const void *value_ = nullptr; - public: - GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value) - : GVArrayImpl(type, size), value_(value) - { - } - - protected: - GVArrayImpl_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArrayImpl(type, size) - { - } - - void get(const int64_t UNUSED(index), void *r_value) const override - { - type_->copy_assign(value_, r_value); - } - void get_to_uninitialized(const int64_t UNUSED(index), void *r_value) const override - { - type_->copy_construct(value_, r_value); - } - - bool is_span() const override - { - return size_ == 1; - } - GSpan get_internal_span() const override - { - return GSpan{*type_, value_, 1}; - } - - bool is_single() const override - { - return true; - } - void get_internal_single(void *r_value) const override - { - type_->copy_assign(value_, r_value); - } +void GVArrayImpl_For_SingleValueRef::get(const int64_t UNUSED(index), void *r_value) const +{ + type_->copy_assign(value_, r_value); +} +void GVArrayImpl_For_SingleValueRef::get_to_uninitialized(const int64_t UNUSED(index), + void *r_value) const +{ + type_->copy_construct(value_, r_value); +} - void materialize(const IndexMask mask, void *dst) const override - { - type_->fill_assign_indices(value_, dst, mask); - } +bool GVArrayImpl_For_SingleValueRef::is_span() const +{ + return size_ == 1; +} +GSpan GVArrayImpl_For_SingleValueRef::get_internal_span() const +{ + return GSpan{*type_, value_, 1}; +} - void materialize_to_uninitialized(const IndexMask mask, void *dst) const override - { - type_->fill_construct_indices(value_, dst, mask); - } +bool GVArrayImpl_For_SingleValueRef::is_single() const +{ + return true; +} +void GVArrayImpl_For_SingleValueRef::get_internal_single(void *r_value) const +{ + type_->copy_assign(value_, r_value); +} - void materialize_compressed(const IndexMask mask, void *dst) const override - { - type_->fill_assign_n(value_, dst, mask.size()); - } +void GVArrayImpl_For_SingleValueRef::materialize(const IndexMask mask, void *dst) const +{ + type_->fill_assign_indices(value_, dst, mask); +} - void materialize_compressed_to_uninitialized(const IndexMask mask, void *dst) const override - { - type_->fill_construct_n(value_, dst, mask.size()); - } -}; +void GVArrayImpl_For_SingleValueRef::materialize_to_uninitialized(const IndexMask mask, + void *dst) const +{ + type_->fill_construct_indices(value_, dst, mask); +} -class GVArrayImpl_For_SingleValueRef_final final : public GVArrayImpl_For_SingleValueRef { - public: - using GVArrayImpl_For_SingleValueRef::GVArrayImpl_For_SingleValueRef; +void GVArrayImpl_For_SingleValueRef::materialize_compressed(const IndexMask mask, void *dst) const +{ + type_->fill_assign_n(value_, dst, mask.size()); +} - private: - bool may_have_ownership() const override - { - return false; - } -}; +void GVArrayImpl_For_SingleValueRef::materialize_compressed_to_uninitialized(const IndexMask mask, + void *dst) const +{ + type_->fill_construct_n(value_, dst, mask.size()); +} /** \} */ @@ -529,8 +478,6 @@ class GVArrayImpl_For_SlicedGVArray : public GVArrayImpl { /** \name #GVArrayCommon * \{ */ -GVArrayCommon::GVArrayCommon() = default; - GVArrayCommon::GVArrayCommon(const GVArrayCommon &other) : storage_(other.storage_) { impl_ = this->impl_from_storage(); @@ -672,17 +619,27 @@ GVArray::GVArray(std::shared_ptr<const GVArrayImpl> impl) : GVArrayCommon(std::m { } -GVArray GVArray::ForSingle(const CPPType &type, const int64_t size, const void *value) +GVArray::GVArray(varray_tag::single /* tag */, + const CPPType &type, + int64_t size, + const void *value) { if (type.is_trivial() && type.size() <= 16 && type.alignment() <= 8) { - return GVArray::For<GVArrayImpl_For_SmallTrivialSingleValue<16>>(type, size, value); + this->emplace<GVArrayImpl_For_SmallTrivialSingleValue<16>>(type, size, value); } - return GVArray::For<GVArrayImpl_For_SingleValue>(type, size, value); + else { + this->emplace<GVArrayImpl_For_SingleValue>(type, size, value); + } +} + +GVArray GVArray::ForSingle(const CPPType &type, const int64_t size, const void *value) +{ + return GVArray(varray_tag::single{}, type, size, value); } GVArray GVArray::ForSingleRef(const CPPType &type, const int64_t size, const void *value) { - return GVArray::For<GVArrayImpl_For_SingleValueRef_final>(type, size, value); + return GVArray(varray_tag::single_ref{}, type, size, value); } GVArray GVArray::ForSingleDefault(const CPPType &type, const int64_t size) @@ -692,10 +649,7 @@ GVArray GVArray::ForSingleDefault(const CPPType &type, const int64_t size) GVArray GVArray::ForSpan(GSpan span) { - /* Use const-cast because the underlying virtual array implementation is shared between const - * and non const data. */ - GMutableSpan mutable_span{span.type(), const_cast<void *>(span.data()), span.size()}; - return GVArray::For<GVArrayImpl_For_GSpan_final>(mutable_span); + return GVArray(varray_tag::span{}, span); } class GVArrayImpl_For_GArray : public GVArrayImpl_For_GSpan { diff --git a/source/blender/blenlib/intern/hash_md5.c b/source/blender/blenlib/intern/hash_md5.c index cc59662b6de..9da8c0a0941 100644 --- a/source/blender/blenlib/intern/hash_md5.c +++ b/source/blender/blenlib/intern/hash_md5.c @@ -271,7 +271,7 @@ static void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf) int BLI_hash_md5_stream(FILE *stream, void *resblock) { -#define BLOCKSIZE 4096 /* Important: must be a multiple of 64. */ +#define BLOCKSIZE 4096 /* IMPORTANT: must be a multiple of 64. */ struct md5_ctx ctx; md5_uint32 len[2]; char buffer[BLOCKSIZE + 72]; diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index e2044955e48..3932e5eb051 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -193,9 +193,19 @@ void BLI_listbases_swaplinks(ListBase *listbasea, ListBase *listbaseb, void *vli return; } + /* The reference to `linkc` assigns NULL, not a dangling pointer so it can be ignored. */ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdangling-pointer" +#endif + /* Temporary link to use as placeholder of the links positions */ BLI_insertlinkafter(listbasea, linka, &linkc); +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */ +# pragma GCC diagnostic pop +#endif + /* Bring linka into linkb position */ BLI_remlink(listbasea, linka); BLI_insertlinkafter(listbaseb, linkb, linka); diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 0955f5d1b5a..bdcf52ec521 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -166,7 +166,7 @@ void ycc_to_rgb(float y, float cb, float cr, float *r_r, float *r_g, float *r_b, b = y + 1.772f * cb - 226.816f; break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } *r_r = r / 255.0f; @@ -238,7 +238,7 @@ void rgb_to_hsl(float r, float g, float b, float *r_h, float *r_s, float *r_l) { const float cmax = max_fff(r, g, b); const float cmin = min_fff(r, g, b); - float h, s, l = min_ff(1.0, (cmax + cmin) / 2.0f); + float h, s, l = min_ff(1.0f, (cmax + cmin) / 2.0f); if (cmax == cmin) { h = s = 0.0f; /* achromatic */ diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index f295734706f..ce9abc36cad 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1496,7 +1496,7 @@ void orthogonalize_m3(float R[3][3], int axis) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } mul_v3_fl(R[0], size[0]); @@ -1580,7 +1580,7 @@ void orthogonalize_m4(float R[4][4], int axis) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } mul_v3_fl(R[0], size[0]); @@ -1654,7 +1654,7 @@ void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize) orthogonalize_stable(R[2], R[0], R[1], normalize); break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } @@ -1672,7 +1672,7 @@ void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize) orthogonalize_stable(R[2], R[0], R[1], normalize); break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } @@ -1734,7 +1734,7 @@ static bool orthogonalize_m3_zero_axes_impl(float *mat[3], const float unit_leng break; } default: { - BLI_assert(0); /* Unreachable! */ + BLI_assert_unreachable(); } } @@ -2338,7 +2338,7 @@ void rotate_m4(float mat[4][4], const char axis, const float angle) } break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 4cd377b109e..92223bdf1d5 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1143,7 +1143,7 @@ void axis_angle_to_mat3_single(float R[3][3], const char axis, const float angle R[2][2] = 1.0f; break; default: - BLI_assert(0); + BLI_assert_unreachable(); break; } } diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index bc07669687e..92fd7f5937b 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -871,7 +871,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const /* Similar code used elsewhere, but this checks for double ups * which historically this function supports so better not change */ - /* warning: this only gives stable direction with single polygons, + /* WARNING: this only gives stable direction with single polygons, * ideally we'd calculate connectivity and each polys normal, see T41047 */ const float *v_prev; diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 8387eb5f4f9..976b9a5cd02 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -1155,7 +1155,7 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base BLI_strncpy(dst + len, base_10 ? units_base_10[order] : units_base_2[order], dst_len - len); } -void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format) +void BLI_str_format_decimal_unit(char dst[7], int number_to_format) { float number_to_format_converted = number_to_format; int order = 0; diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c index a6e68401368..7a23b4bb4ad 100644 --- a/source/blender/blenlib/intern/string_cursor_utf8.c +++ b/source/blender/blenlib/intern/string_cursor_utf8.c @@ -198,7 +198,7 @@ void BLI_str_cursor_step_utf8(const char *str, } } else { - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -296,6 +296,6 @@ void BLI_str_cursor_step_utf32(const char32_t *str, } } else { - BLI_assert(0); + BLI_assert_unreachable(); } } diff --git a/source/blender/blenlib/intern/threads.cc b/source/blender/blenlib/intern/threads.cc index 70c1e701348..37fccf6f4fe 100644 --- a/source/blender/blenlib/intern/threads.cc +++ b/source/blender/blenlib/intern/threads.cc @@ -348,7 +348,7 @@ static ThreadMutex *global_mutex_from_type(const int type) case LOCK_VIEW3D: return &_view3d_lock; default: - BLI_assert(0); + BLI_assert_unreachable(); return nullptr; } } diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc index aa7291e1b41..20e2a4d88f8 100644 --- a/source/blender/blenlib/tests/BLI_array_store_test.cc +++ b/source/blender/blenlib/tests/BLI_array_store_test.cc @@ -644,7 +644,7 @@ static void testbuffer_list_state_random_data(ListBase *lb, break; } default: - BLI_assert(0); + BLI_assert_unreachable(); } } } diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc new file mode 100644 index 00000000000..d22993ee69e --- /dev/null +++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "BLI_float3x3.hh" +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + +namespace blender::tests { + +TEST(float3x3, Identity) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::identity(); + float2 result = transformation * point; + EXPECT_EQ(result, point); +} + +TEST(float3x3, Translation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 6.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, Rotation) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -2.0f); + EXPECT_FLOAT_EQ(result[1], 1.0f); +} + +TEST(float3x3, TranslationRotationScale) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_2, float2(2.0f, 3.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 5.0f); +} + +TEST(float3x3, NormalizedAxes) +{ + float2 point(1.0f, 2.0f); + + /* The horizontal is aligned with (1, 1) and vertical is aligned with (-1, 1), in other words, a + * Pi / 4 rotation. */ + float value = std::sqrt(2.0f) / 2.0f; + float3x3 transformation = float3x3::from_normalized_axes( + float2(1.0f, 3.0f), float2(value), float2(-value, value)); + float2 result = transformation * point; + + float3x3 expected_transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + float2 expected = expected_transformation * point; + + EXPECT_FLOAT_EQ(result[0], expected[0]); + EXPECT_FLOAT_EQ(result[1], expected[1]); +} + +TEST(float3x3, PostTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = translation * rotation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, PreTransformationMultiplication) +{ + float2 point(1.0f, 2.0f); + float3x3 translation = float3x3::from_translation(float2(5.0f, 3.0f)); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = rotation * translation; + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], -5.0f); + EXPECT_FLOAT_EQ(result[1], 6.0f); +} + +TEST(float3x3, TransformationMultiplicationAssignment) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation(float2(5.0f, 3.0f)); + transformation *= float3x3::from_rotation(M_PI_2); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 3.0f); + EXPECT_FLOAT_EQ(result[1], 4.0f); +} + +TEST(float3x3, Inverted) +{ + float2 point(1.0f, 2.0f); + float3x3 transformation = float3x3::from_translation_rotation_scale( + float2(1.0f, 3.0f), M_PI_4, float2(1.0f)); + transformation *= transformation.inverted(); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 1.0f); + EXPECT_FLOAT_EQ(result[1], 2.0f); +} + +TEST(float3x3, Origin) +{ + float2 point(1.0f, 2.0f); + float3x3 rotation = float3x3::from_rotation(M_PI_2); + float3x3 transformation = float3x3::from_origin_transformation(rotation, float2(0.0f, 2.0f)); + float2 result = transformation * point; + EXPECT_FLOAT_EQ(result[0], 0.0f); + EXPECT_FLOAT_EQ(result[1], 3.0f); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index bfd297214c0..4f6f4a5c413 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -663,7 +663,7 @@ TEST(path_util, PathContains) EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) << "A path contains its subdirectory"; EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) << "Windows paths should be supported as well"; @@ -672,7 +672,7 @@ TEST(path_util, PathContains) EXPECT_FALSE(BLI_path_contains("/some/path", "/")) << "Root directory not be contained in a subdirectory"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) - << "Paths should be normalised"; + << "Paths should be normalized"; EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) << "Just sharing a suffix is not enough, path semantics should be followed"; EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) diff --git a/source/blender/blenlib/tests/BLI_string_test.cc b/source/blender/blenlib/tests/BLI_string_test.cc index 6c16af5767c..eaaa65dd39f 100644 --- a/source/blender/blenlib/tests/BLI_string_test.cc +++ b/source/blender/blenlib/tests/BLI_string_test.cc @@ -420,98 +420,98 @@ TEST(string, StrFormatByteUnits) EXPECT_STREQ("-8191.8472 PiB", size_str); } -/* BLI_str_format_attribute_domain_size */ -TEST(string, StrFormatAttributeDomainSize) +/* BLI_str_format_decimal_unit */ +TEST(string, StrFormatDecimalUnits) { char size_str[7]; int size; - BLI_str_format_attribute_domain_size(size_str, size = 0); + BLI_str_format_decimal_unit(size_str, size = 0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1); + BLI_str_format_decimal_unit(size_str, size = 1); EXPECT_STREQ("1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10); + BLI_str_format_decimal_unit(size_str, size = 10); EXPECT_STREQ("10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15); + BLI_str_format_decimal_unit(size_str, size = 15); EXPECT_STREQ("15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100); + BLI_str_format_decimal_unit(size_str, size = 100); EXPECT_STREQ("100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155); + BLI_str_format_decimal_unit(size_str, size = 155); EXPECT_STREQ("155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000); + BLI_str_format_decimal_unit(size_str, size = 1000); EXPECT_STREQ("1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555); + BLI_str_format_decimal_unit(size_str, size = 1555); EXPECT_STREQ("1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000); + BLI_str_format_decimal_unit(size_str, size = 10000); EXPECT_STREQ("10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555); + BLI_str_format_decimal_unit(size_str, size = 15555); EXPECT_STREQ("15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000); + BLI_str_format_decimal_unit(size_str, size = 100000); EXPECT_STREQ("100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555); + BLI_str_format_decimal_unit(size_str, size = 155555); EXPECT_STREQ("156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000); + BLI_str_format_decimal_unit(size_str, size = 1000000); EXPECT_STREQ("1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1555555); + BLI_str_format_decimal_unit(size_str, size = 1555555); EXPECT_STREQ("1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 10000000); + BLI_str_format_decimal_unit(size_str, size = 10000000); EXPECT_STREQ("10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 15555555); + BLI_str_format_decimal_unit(size_str, size = 15555555); EXPECT_STREQ("15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 100000000); + BLI_str_format_decimal_unit(size_str, size = 100000000); EXPECT_STREQ("100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 155555555); + BLI_str_format_decimal_unit(size_str, size = 155555555); EXPECT_STREQ("156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = 1000000000); + BLI_str_format_decimal_unit(size_str, size = 1000000000); EXPECT_STREQ("1.0B", size_str); /* Largest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = INT32_MAX); EXPECT_STREQ("2.1B", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -0); + BLI_str_format_decimal_unit(size_str, size = -0); EXPECT_STREQ("0", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1); + BLI_str_format_decimal_unit(size_str, size = -1); EXPECT_STREQ("-1", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10); + BLI_str_format_decimal_unit(size_str, size = -10); EXPECT_STREQ("-10", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15); + BLI_str_format_decimal_unit(size_str, size = -15); EXPECT_STREQ("-15", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100); + BLI_str_format_decimal_unit(size_str, size = -100); EXPECT_STREQ("-100", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155); + BLI_str_format_decimal_unit(size_str, size = -155); EXPECT_STREQ("-155", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000); + BLI_str_format_decimal_unit(size_str, size = -1000); EXPECT_STREQ("-1.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555); + BLI_str_format_decimal_unit(size_str, size = -1555); EXPECT_STREQ("-1.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000); + BLI_str_format_decimal_unit(size_str, size = -10000); EXPECT_STREQ("-10.0K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555); + BLI_str_format_decimal_unit(size_str, size = -15555); EXPECT_STREQ("-15.6K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000); + BLI_str_format_decimal_unit(size_str, size = -100000); EXPECT_STREQ("-100K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555); + BLI_str_format_decimal_unit(size_str, size = -155555); EXPECT_STREQ("-156K", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000); + BLI_str_format_decimal_unit(size_str, size = -1000000); EXPECT_STREQ("-1.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1555555); + BLI_str_format_decimal_unit(size_str, size = -1555555); EXPECT_STREQ("-1.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -10000000); + BLI_str_format_decimal_unit(size_str, size = -10000000); EXPECT_STREQ("-10.0M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -15555555); + BLI_str_format_decimal_unit(size_str, size = -15555555); EXPECT_STREQ("-15.6M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -100000000); + BLI_str_format_decimal_unit(size_str, size = -100000000); EXPECT_STREQ("-100M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -155555555); + BLI_str_format_decimal_unit(size_str, size = -155555555); EXPECT_STREQ("-156M", size_str); - BLI_str_format_attribute_domain_size(size_str, size = -1000000000); + BLI_str_format_decimal_unit(size_str, size = -1000000000); EXPECT_STREQ("-1.0B", size_str); /* Smallest possible value. */ - BLI_str_format_attribute_domain_size(size_str, size = -INT32_MAX); + BLI_str_format_decimal_unit(size_str, size = -INT32_MAX); EXPECT_STREQ("-2.1B", size_str); } diff --git a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc index 0ff488202c2..09bb1e7239f 100644 --- a/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc +++ b/source/blender/blenlib/tests/performance/BLI_ghash_performance_test.cc @@ -57,23 +57,23 @@ static void str_ghash_tests(GHash *ghash, const char *id) printf("\n========== STARTING %s ==========\n", id); #ifdef TEXT_CORPUS_PATH - size_t sz = 0; + size_t data_size = 0; char *data; { struct stat st; if (stat(TEXT_CORPUS_PATH, &st) == 0) - sz = st.st_size; + data_size = st.st_size; } - if (sz != 0) { + if (data_size != 0) { FILE *f = fopen(TEXT_CORPUS_PATH, "r"); - data = (char *)MEM_mallocN(sz + 1, __func__); - if (fread(data, sizeof(*data), sz, f) != sz) { + data = (char *)MEM_mallocN(data_size + 1, __func__); + if (fread(data, sizeof(*data), data_size, f) != data_size) { printf("ERROR in reading file %s!", TEXT_CORPUS_PATH); MEM_freeN(data); data = BLI_strdup(words10k); } - data[sz] = '\0'; + data[data_size] = '\0'; fclose(f); } else { diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index c1274de034d..043f9ffd723 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -156,10 +156,11 @@ BlendFileData *BLO_read_from_memory(const void *mem, * * \param oldmain: old main, * from which we will keep libraries and other data-blocks that should not have changed. - * \param filename: current file, only for retrieving library data. + * \param filepath: current file, only for retrieving library data. + * Typically `BKE_main_blendfile_path(oldmain)`. */ BlendFileData *BLO_read_from_memfile(struct Main *oldmain, - const char *filename, + const char *filepath, struct MemFile *memfile, const struct BlendFileReadParams *params, struct ReportList *reports); diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index 48334444c4c..0584f95d85f 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -46,7 +46,7 @@ typedef struct MemFileWriteData { } MemFileWriteData; typedef struct MemFileUndoData { - char filename[1024]; /* FILE_MAX */ + char filepath[1024]; /* FILE_MAX */ MemFile memfile; size_t undo_size; } MemFileUndoData; @@ -98,6 +98,6 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, * * \return success. */ -extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename); +extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath); FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 10b69b67fa1..1bfa3b0e2bb 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -388,7 +388,7 @@ BlendFileData *BLO_read_from_memory(const void *mem, } BlendFileData *BLO_read_from_memfile(Main *oldmain, - const char *filename, + const char *filepath, MemFile *memfile, const struct BlendFileReadParams *params, ReportList *reports) @@ -401,7 +401,7 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, fd = blo_filedata_from_memfile(memfile, params, &bf_reports); if (fd) { fd->skip_flags = params->skip_flags; - BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase)); + BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase)); /* separate libraries from old main */ blo_split_main(&old_mainlist, oldmain); @@ -420,7 +420,7 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, * read IDs whenever possible. */ blo_cache_storage_init(fd, oldmain); - bfd = blo_read_file_internal(fd, filename); + bfd = blo_read_file_internal(fd, filepath); /* Ensure relinked caches are not freed together with their old IDs. */ blo_cache_storage_old_bmain_clear(fd, oldmain); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 27890a908ab..973965ada50 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -477,7 +477,6 @@ static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint li } else { CLOG_ERROR(&LOG, "Invalid library for '%s'", id->name); - BLI_assert(0); } } } @@ -1477,14 +1476,14 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) const int width = fd_data[0]; const int height = fd_data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - data = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + data = MEM_mallocN(data_size, __func__); if (data) { - BLI_assert((sz - sizeof(*data)) == + BLI_assert((data_size - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2))); data->width = width; data->height = height; - memcpy(data->rect, &fd_data[2], sz - sizeof(*data)); + memcpy(data->rect, &fd_data[2], data_size - sizeof(*data)); } } } @@ -2068,7 +2067,7 @@ static void direct_link_id_embedded_id(BlendDataReader *reader, static int direct_link_id_restore_recalc_exceptions(const ID *id_current) { /* Exception for armature objects, where the pose has direct points to the - * armature databolock. */ + * armature data-block. */ if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) { return ID_RECALC_GEOMETRY; } @@ -2413,7 +2412,7 @@ static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data) if (collection->flag & COLLECTION_IS_MASTER) { /* We should never reach that point anymore, since master collection private ID should be * properly tagged with IDWALK_CB_EMBEDDED. */ - BLI_assert(0); + BLI_assert_unreachable(); return IDWALK_RET_NOP; } } @@ -3857,14 +3856,14 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) const int width = data[0]; const int height = data[1]; if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) { - const size_t sz = BLEN_THUMB_MEMSIZE(width, height); - bfd->main->blen_thumb = MEM_mallocN(sz, __func__); + const size_t data_size = BLEN_THUMB_MEMSIZE(width, height); + bfd->main->blen_thumb = MEM_mallocN(data_size, __func__); - BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) == + BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2))); bfd->main->blen_thumb->width = width; bfd->main->blen_thumb->height = height; - memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb)); + memcpy(bfd->main->blen_thumb->rect, &data[2], data_size - sizeof(*bfd->main->blen_thumb)); } } } diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 5c4598b85c5..b5c2a73c268 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -120,7 +120,7 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data, *entry = mem_chunk; } else { - BLI_assert(0); + BLI_assert_unreachable(); } } } @@ -195,7 +195,7 @@ struct Main *BLO_memfile_main_get(struct MemFile *memfile, return bmain_undo; } -bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) +bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath) { MemFileChunk *chunk; int file, oflags; @@ -216,12 +216,12 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) # warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103" # endif #endif - file = BLI_open(filename, oflags, 0666); + file = BLI_open(filepath, oflags, 0666); if (file == -1) { fprintf(stderr, "Unable to save '%s': %s\n", - filename, + filepath, errno ? strerror(errno) : "Unknown error opening file"); return false; } @@ -242,7 +242,7 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) if (chunk) { fprintf(stderr, "Unable to save '%s': %s\n", - filename, + filepath, errno ? strerror(errno) : "Unknown error writing file"); return false; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d992d426b9a..eeec55a7b06 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4892,7 +4892,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) /* Default Face Set Color. */ for (Mesh *me = bmain->meshes.first; me != NULL; me = me->id.next) { if (me->totpoly > 0) { - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); if (face_sets) { me->face_sets_color_default = abs(face_sets[0]); } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 2f6f0d5c9fa..585ada3b2d8 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -360,8 +360,8 @@ static void seq_update_meta_disp_range(Editing *ed) } /* Update meta strip endpoints. */ - SEQ_transform_set_left_handle_frame(ms->parseq, ms->disp_range[0]); - SEQ_transform_set_right_handle_frame(ms->parseq, ms->disp_range[1]); + SEQ_time_left_handle_frame_set(ms->parseq, ms->disp_range[0]); + SEQ_time_right_handle_frame_set(ms->parseq, ms->disp_range[1]); SEQ_transform_fix_single_image_seq_offsets(ms->parseq); /* Recalculate effects using meta strip. */ @@ -1666,13 +1666,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { - /* UV/Image Max resolution images in image editor. */ - if (space->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)space; - sima->iuser.flag |= IMA_SHOW_MAX_RESOLUTION; - } /* Enable Outliner render visibility column. */ - else if (space->spacetype == SPACE_OUTLINER) { + if (space->spacetype == SPACE_OUTLINER) { SpaceOutliner *space_outliner = (SpaceOutliner *)space; space_outliner->show_restrict_flags |= SO_RESTRICT_RENDER; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index e6a214452fe..83d325e9c40 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -63,7 +63,7 @@ #include "RNA_prototypes.h" #include "BLO_readfile.h" -#include "MEM_guardedalloc.h" + #include "readfile.h" #include "SEQ_channels.h" @@ -71,8 +71,6 @@ #include "SEQ_sequencer.h" #include "SEQ_time.h" -#include "RNA_access.h" - #include "versioning_common.h" static CLG_LogRef LOG = {"blo.readfile.doversion"}; @@ -1217,6 +1215,15 @@ static bool version_fix_seq_meta_range(Sequence *seq, void *user_data) return true; } +static bool version_merge_still_offsets(Sequence *seq, void *UNUSED(user_data)) +{ + seq->startofs -= seq->startstill; + seq->endofs -= seq->endstill; + seq->startstill = 0; + seq->endstill = 0; + return true; +} + /* Those `version_liboverride_rnacollections_*` functions mimic the old, pre-3.0 code to find * anchor and source items in the given list of modifiers, constraints etc., using only the * `subitem_local` data of the override property operation. @@ -2735,6 +2742,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END; + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_SUBDIVISION_SURFACE, "Crease", "Edge Crease"); + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 302, 13)) { @@ -2772,5 +2786,279 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Replace legacy combine/separate color nodes */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + /* In geometry nodes, replace shader combine/separate color nodes with function nodes */ + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = FN_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = FN_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "FunctionNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In compositing nodes, replace combine/separate RGBA/HSVA/YCbCrA/YCCA nodes with + * combine/separate color */ + if (ntree->type == NTREE_COMPOSIT) { + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cb", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cr", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "A", "Alpha"); + + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "Y", "Red"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "U", "Green"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "V", "Blue"); + version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cb", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cr", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "A", "Alpha"); + + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "Y", "Red"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "U", "Green"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "V", "Blue"); + version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "A", "Alpha"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case CMP_NODE_COMBRGBA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBHSVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYCCA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_COMBYUVA_LEGACY: { + node->type = CMP_NODE_COMBINE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeCombineColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPRGBA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPHSVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYCCA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YCC; + storage->ycc_mode = node->custom1; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + case CMP_NODE_SEPYUVA_LEGACY: { + node->type = CMP_NODE_SEPARATE_COLOR; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)MEM_callocN( + sizeof(NodeCMPCombSepColor), __func__); + storage->mode = CMP_NODE_COMBSEP_COLOR_YUV; + strcpy(node->idname, "CompositorNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + + /* In texture nodes, replace combine/separate RGBA with combine/separate color */ + if (ntree->type == NTREE_TEXTURE) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case TEX_NODE_COMPOSE_LEGACY: { + node->type = TEX_NODE_COMBINE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeCombineColor"); + break; + } + case TEX_NODE_DECOMPOSE_LEGACY: { + node->type = TEX_NODE_SEPARATE_COLOR; + node->custom1 = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "TextureNodeSeparateColor"); + break; + } + } + } + } + + /* In shader nodes, replace combine/separate RGB/HSV with combine/separate color */ + if (ntree->type == NTREE_SHADER) { + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue"); + version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color"); + + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "H", "Red"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "S", "Green"); + version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "V", "Blue"); + + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue"); + version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color"); + + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "H", "Red"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "S", "Green"); + version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "V", "Blue"); + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + switch (node->type) { + case SH_NODE_COMBRGB_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_COMBHSV_LEGACY: { + node->type = SH_NODE_COMBINE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeCombineColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPRGB_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_RGB; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + case SH_NODE_SEPHSV_LEGACY: { + node->type = SH_NODE_SEPARATE_COLOR; + NodeCombSepColor *storage = (NodeCombSepColor *)MEM_callocN(sizeof(NodeCombSepColor), + __func__); + storage->mode = NODE_COMBSEP_COLOR_HSV; + strcpy(node->idname, "ShaderNodeSeparateColor"); + node->storage = storage; + break; + } + } + } + } + } + FOREACH_NODETREE_END; + + /* Initialize brush curves sculpt settings. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->ob_mode != OB_MODE_SCULPT_CURVES) { + continue; + } + if (brush->curves_sculpt_settings->points_per_curve == 0) { + brush->curves_sculpt_settings->points_per_curve = 8; + } + } + + /* UDIM Packing. */ + if (!DNA_struct_elem_find(fd->filesdna, "ImagePackedFile", "int", "tile_number")) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + int view; + LISTBASE_FOREACH_INDEX (ImagePackedFile *, imapf, &ima->packedfiles, view) { + imapf->view = view; + imapf->tile_number = 1001; + } + } + } + + /* Merge still offsets into start/end offsets. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + Editing *ed = SEQ_editing_get(scene); + if (ed != NULL) { + SEQ_for_each_callback(&ed->seqbase, version_merge_still_offsets, NULL); + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 22db54d7609..65c42545a77 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -73,6 +73,8 @@ #include "BLI_utildefines.h" +#include "CLG_log.h" + /* allow writefile to use deprecated functionality (for forward compatibility code) */ #define DNA_DEPRECATED_ALLOW @@ -133,6 +135,8 @@ #define ZSTD_COMPRESSION_LEVEL 3 +static CLG_LogRef LOG = {"blo.writefile"}; + /** Use if we want to store how many bytes have been written to the file. */ // #define USE_WRITE_DATA_LEN @@ -973,7 +977,7 @@ static void write_libraries(WriteData *wd, Main *main) if (main->curlib->packedfile) { BKE_packedfile_blend_write(&writer, main->curlib->packedfile); if (wd->use_memfile == false) { - printf("write packed .blend: %s\n", main->curlib->filepath); + CLOG_INFO(&LOG, 2, "Write packed .blend: %s\n", main->curlib->filepath); } } @@ -984,12 +988,11 @@ static void write_libraries(WriteData *wd, Main *main) ((id->tag & LIB_TAG_EXTERN) || ((id->tag & LIB_TAG_INDIRECT) && (id->flag & LIB_INDIRECT_WEAK_LINK)))) { if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { - printf( - "ERROR: write file: data-block '%s' from lib '%s' is not linkable " - "but is flagged as directly linked\n", - id->name, - main->curlib->filepath_abs); - BLI_assert(0); + CLOG_ERROR(&LOG, + "Data-block '%s' from lib '%s' is not linkable, but is flagged as " + "directly linked\n", + id->name, + main->curlib->filepath_abs); } writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id); } @@ -1129,9 +1132,15 @@ static bool write_file_handle(Main *mainvar, char id_buffer_static[ID_BUFFER_STATIC_SIZE]; void *id_buffer = id_buffer_static; - const size_t idtype_struct_size = BKE_idtype_get_info_from_id(id)->struct_size; + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + const size_t idtype_struct_size = id_type->struct_size; if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) { - BLI_assert(0); + CLOG_ERROR(&LOG, + "ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, " + "which needs %lu bytes", + ID_BUFFER_STATIC_SIZE, + id_type->name, + id_type->struct_size); id_buffer = MEM_mallocN(idtype_struct_size, __func__); } @@ -1200,7 +1209,6 @@ static bool write_file_handle(Main *mainvar, * #direct_link_id_common in `readfile.c` anyway, */ ((ID *)id_buffer)->py_instance = NULL; - const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->blend_write != NULL) { id_type->blend_write(&writer, (ID *)id_buffer, id); } @@ -1415,7 +1423,7 @@ bool BLO_write_file(Main *mainvar, BKE_bpath_absolute_convert(mainvar, dir_src, NULL); break; case BLO_WRITE_PATH_REMAP_NONE: - BLI_assert(0); /* Unreachable. */ + BLI_assert_unreachable(); /* Unreachable. */ break; } @@ -1490,7 +1498,7 @@ void BLO_write_struct_array_by_name(BlendWriter *writer, { int struct_id = BLO_get_struct_id_by_name(writer, struct_name); if (UNLIKELY(struct_id == -1)) { - printf("error: can't find SDNA code <%s>\n", struct_name); + CLOG_ERROR(&LOG, "Can't find SDNA code <%s>\n", struct_name); return; } BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr); @@ -1538,7 +1546,7 @@ void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, { int struct_id = BLO_get_struct_id_by_name(writer, struct_name); if (UNLIKELY(struct_id == -1)) { - printf("error: can't find SDNA code <%s>\n", struct_name); + CLOG_ERROR(&LOG, "Can't find SDNA code <%s>\n", struct_name); return; } BLO_write_struct_list_by_id(writer, struct_id, list); diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 0246850123a..9d5737a5b71 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -265,8 +265,20 @@ typedef struct BMFace { * (the length of #BMFace.l_first circular linked list). */ int len; - float no[3]; /* face normal */ - short mat_nr; /* material index */ + /** + * Face normal, see #BM_face_calc_normal. + */ + float no[3]; + /** + * Material index, typically >= 0 and < #Mesh.totcol although this isn't enforced + * Python for e.g. can set this to any positive value since scripts may create + * mesh data first and setup material slots later. + * + * When using to index into a material array it's range should be checked first, + * values exceeding the range should be ignored or treated as zero + * (if a material slot needs to be used - when drawing for e.g.) + */ + short mat_nr; // short _pad[3]; } BMFace; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index d6bf556f14b..4d84d558cd7 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -1460,7 +1460,7 @@ BMFace *bmesh_kernel_split_face_make_edge(BMesh *bm, } else { /* this code is not significant until holes actually work */ - // printf("warning: call to split face euler without holes argument; holes will be tossed.\n"); + // printf("WARNING: call to split face euler without holes argument; holes will be tossed.\n"); for (lst = f->loops.last; lst != f->loops.first; lst = lst2) { lst2 = lst->prev; BLI_mempool_free(bm->looplistpool, lst); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 40f1d7c496d..bb1fe749e6b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -225,9 +225,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar return; /* Sanity check. */ } - /* Only copy normals to the new BMesh if they are not already dirty. This avoids unnecessary - * work, but also accessing normals on an incomplete mesh, for example when restoring undo steps - * in edit mode. */ const float(*vert_normals)[3] = nullptr; if (params->calc_vert_normal) { vert_normals = BKE_mesh_vertex_normals_ensure(me); diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index 70710edea5c..8b56e08c22b 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -597,7 +597,7 @@ bool BM_face_validate(BMFace *face, FILE *err) bool ret = true; if (face->len == 2) { - fprintf(err, "warning: found two-edged face. face ptr: %p\n", face); + fprintf(err, "WARNING: found two-edged face. face ptr: %p\n", face); fflush(err); } diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 9ea16c8b61c..6df446a377c 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -415,7 +415,7 @@ void BM_face_calc_tangent_edge_diagonal(const BMFace *f, float r_tangent[3]) /* In case of degenerate faces. */ zero_v3(r_tangent); - /* warning: O(n^2) loop here, take care! */ + /* WARNING: O(n^2) loop here, take care! */ float dist_max_sq = 0.0f; do { BMLoop *l_iter_other = l_iter->next; @@ -447,7 +447,7 @@ void BM_face_calc_tangent_vert_diagonal(const BMFace *f, float r_tangent[3]) /* In case of degenerate faces. */ zero_v3(r_tangent); - /* warning: O(n^2) loop here, take care! */ + /* WARNING: O(n^2) loop here, take care! */ float dist_max_sq = 0.0f; do { BMLoop *l_iter_other = l_iter->next; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 8f32f878c58..fa852cdd6da 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -5354,7 +5354,7 @@ static BMEdge *snap_edge_for_center_vmesh_vert(int i, * so the arguments bndv_rep_faces is an array of size n_bndv give the freps for each i, * and center_frep is the frep for the center. * - * Note: this function is for edge bevels only, at the moment. + * NOTE: this function is for edge bevels only, at the moment. */ static void snap_edges_for_vmesh_vert(int i, int j, diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 66f813e02b2..55e349423bb 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -259,12 +259,16 @@ set(SRC # converter nodes nodes/COM_CombineColorNode.cc nodes/COM_CombineColorNode.h + nodes/COM_CombineColorNodeLegacy.cc + nodes/COM_CombineColorNodeLegacy.h nodes/COM_CombineXYZNode.cc nodes/COM_CombineXYZNode.h nodes/COM_IDMaskNode.cc nodes/COM_IDMaskNode.h nodes/COM_SeparateColorNode.cc nodes/COM_SeparateColorNode.h + nodes/COM_SeparateColorNodeLegacy.cc + nodes/COM_SeparateColorNodeLegacy.h nodes/COM_SeparateXYZNode.cc nodes/COM_SeparateXYZNode.h diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 13d412a338b..6d7341376e9 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -29,6 +29,7 @@ #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" #include "COM_CombineColorNode.h" +#include "COM_CombineColorNodeLegacy.h" #include "COM_CombineXYZNode.h" #include "COM_CompositorNode.h" #include "COM_ConvertAlphaNode.h" @@ -82,6 +83,7 @@ #include "COM_ScaleOperation.h" #include "COM_SceneTimeNode.h" #include "COM_SeparateColorNode.h" +#include "COM_SeparateColorNodeLegacy.h" #include "COM_SeparateXYZNode.h" #include "COM_SetAlphaNode.h" #include "COM_SetValueOperation.h" @@ -169,28 +171,34 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_BRIGHTCONTRAST: node = new BrightnessNode(b_node); break; - case CMP_NODE_SEPRGBA: + case CMP_NODE_SEPARATE_COLOR: + node = new SeparateColorNode(b_node); + break; + case CMP_NODE_COMBINE_COLOR: + node = new CombineColorNode(b_node); + break; + case CMP_NODE_SEPRGBA_LEGACY: node = new SeparateRGBANode(b_node); break; - case CMP_NODE_COMBRGBA: + case CMP_NODE_COMBRGBA_LEGACY: node = new CombineRGBANode(b_node); break; - case CMP_NODE_SEPHSVA: + case CMP_NODE_SEPHSVA_LEGACY: node = new SeparateHSVANode(b_node); break; - case CMP_NODE_COMBHSVA: + case CMP_NODE_COMBHSVA_LEGACY: node = new CombineHSVANode(b_node); break; - case CMP_NODE_SEPYUVA: + case CMP_NODE_SEPYUVA_LEGACY: node = new SeparateYUVANode(b_node); break; - case CMP_NODE_COMBYUVA: + case CMP_NODE_COMBYUVA_LEGACY: node = new CombineYUVANode(b_node); break; - case CMP_NODE_SEPYCCA: + case CMP_NODE_SEPYCCA_LEGACY: node = new SeparateYCCANode(b_node); break; - case CMP_NODE_COMBYCCA: + case CMP_NODE_COMBYCCA_LEGACY: node = new CombineYCCANode(b_node); break; case CMP_NODE_ALPHAOVER: diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 66dbabe71b5..d0f0be590f6 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -420,7 +420,7 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) char *str = (char *)MEM_mallocN(max_textlength, __func__); if (graphviz_system(system, str, max_textlength - 1)) { char basename[FILE_MAX]; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; if (name.is_empty()) { BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", file_index_); @@ -428,12 +428,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) else { BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename)); } - BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename); + BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_session(), basename); file_index_++; - std::cout << "Writing compositor debug to: " << filename << "\n"; + std::cout << "Writing compositor debug to: " << filepath << "\n"; - FILE *fp = BLI_fopen(filename, "wb"); + FILE *fp = BLI_fopen(filepath, "wb"); fputs(str, fp); fclose(fp); } diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index 0c8748fd2d6..ca2c59478fd 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -12,7 +12,7 @@ CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node) } void CombineColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *input_rsocket = this->get_input_socket(0); NodeInput *input_gsocket = this->get_input_socket(1); @@ -40,7 +40,39 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertHSVToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertHSLToRGBOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertYUVToRGBOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -52,27 +84,4 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertHSVToRGBOperation(); -} - -NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertYUVToRGBOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h index 2bead24cb02..7576cc9eb2d 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.h +++ b/source/blender/compositor/nodes/COM_CombineColorNode.h @@ -12,45 +12,6 @@ class CombineColorNode : public Node { CombineColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class CombineRGBANode : public CombineColorNode { - public: - CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineHSVANode : public CombineColorNode { - public: - CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYCCANode : public CombineColorNode { - public: - CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class CombineYUVANode : public CombineColorNode { - public: - CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc new file mode 100644 index 00000000000..d5ba379bfc2 --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_CombineColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +CombineColorNodeLegacy::CombineColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void CombineColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *input_rsocket = this->get_input_socket(0); + NodeInput *input_gsocket = this->get_input_socket(1); + NodeInput *input_bsocket = this->get_input_socket(2); + NodeInput *input_asocket = this->get_input_socket(3); + NodeOutput *output_socket = this->get_output_socket(0); + + CombineChannelsOperation *operation = new CombineChannelsOperation(); + if (input_rsocket->is_linked()) { + operation->set_canvas_input_index(0); + } + else if (input_gsocket->is_linked()) { + operation->set_canvas_input_index(1); + } + else if (input_bsocket->is_linked()) { + operation->set_canvas_input_index(2); + } + else { + operation->set_canvas_input_index(3); + } + converter.add_operation(operation); + + converter.map_input_socket(input_rsocket, operation->get_input_socket(0)); + converter.map_input_socket(input_gsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); + converter.map_input_socket(input_asocket, operation->get_input_socket(3)); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0)); + converter.map_output_socket(output_socket, color_conv->get_output_socket()); + } + else { + converter.map_output_socket(output_socket, operation->get_output_socket()); + } +} + +NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertHSVToRGBOperation(); +} + +NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertYUVToRGBOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h new file mode 100644 index 00000000000..edc66c8932f --- /dev/null +++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class CombineColorNodeLegacy : public Node { + public: + CombineColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class CombineRGBANode : public CombineColorNodeLegacy { + public: + CombineRGBANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineHSVANode : public CombineColorNodeLegacy { + public: + CombineHSVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYCCANode : public CombineColorNodeLegacy { + public: + CombineYCCANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class CombineYUVANode : public CombineColorNodeLegacy { + public: + CombineYUVANode(bNode *editor_node) : CombineColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index 221d80e67f2..a956c02ed42 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -12,7 +12,7 @@ SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node) } void SeparateColorNode::convert_to_operations(NodeConverter &converter, - const CompositorContext &context) const + const CompositorContext &UNUSED(context)) const { NodeInput *image_socket = this->get_input_socket(0); NodeOutput *output_rsocket = this->get_output_socket(0); @@ -20,7 +20,39 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, NodeOutput *output_bsocket = this->get_output_socket(2); NodeOutput *output_asocket = this->get_output_socket(3); - NodeOperation *color_conv = get_color_converter(context); + bNode *editor_node = this->get_bnode(); + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage; + + NodeOperation *color_conv = nullptr; + switch (storage->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case CMP_NODE_COMBSEP_COLOR_HSV: { + color_conv = new ConvertRGBToHSVOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_HSL: { + color_conv = new ConvertRGBToHSLOperation(); + break; + } + case CMP_NODE_COMBSEP_COLOR_YCC: { + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + operation->set_mode(storage->ycc_mode); + color_conv = operation; + break; + } + case CMP_NODE_COMBSEP_COLOR_YUV: { + color_conv = new ConvertRGBToYUVOperation(); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } + if (color_conv) { converter.add_operation(color_conv); @@ -84,27 +116,4 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter, } } -NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return nullptr; /* no conversion needed */ -} - -NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToHSVOperation(); -} - -NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const -{ - ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editor_node = this->get_bnode(); - operation->set_mode(editor_node->custom1); - return operation; -} - -NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const -{ - return new ConvertRGBToYUVOperation(); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h index 78ab0959f43..6adb2d0bb22 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.h +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h @@ -12,45 +12,6 @@ class SeparateColorNode : public Node { SeparateColorNode(bNode *editor_node); void convert_to_operations(NodeConverter &converter, const CompositorContext &context) const override; - - protected: - virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; -}; - -class SeparateRGBANode : public SeparateColorNode { - public: - SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateHSVANode : public SeparateColorNode { - public: - SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYCCANode : public SeparateColorNode { - public: - SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; -}; - -class SeparateYUVANode : public SeparateColorNode { - public: - SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node) - { - } - - NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc new file mode 100644 index 00000000000..c3728bc152f --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#include "COM_SeparateColorNodeLegacy.h" + +#include "COM_ConvertOperation.h" + +namespace blender::compositor { + +SeparateColorNodeLegacy::SeparateColorNodeLegacy(bNode *editor_node) : Node(editor_node) +{ +} + +void SeparateColorNodeLegacy::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const +{ + NodeInput *image_socket = this->get_input_socket(0); + NodeOutput *output_rsocket = this->get_output_socket(0); + NodeOutput *output_gsocket = this->get_output_socket(1); + NodeOutput *output_bsocket = this->get_output_socket(2); + NodeOutput *output_asocket = this->get_output_socket(3); + + NodeOperation *color_conv = get_color_converter(context); + if (color_conv) { + converter.add_operation(color_conv); + + converter.map_input_socket(image_socket, color_conv->get_input_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(0); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_rsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(1); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_gsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(2); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_bsocket, operation->get_output_socket(0)); + } + + { + SeparateChannelOperation *operation = new SeparateChannelOperation(); + operation->set_channel(3); + converter.add_operation(operation); + + if (color_conv) { + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); + } + else { + converter.map_input_socket(image_socket, operation->get_input_socket(0)); + } + converter.map_output_socket(output_asocket, operation->get_output_socket(0)); + } +} + +NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return nullptr; /* no conversion needed */ +} + +NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToHSVOperation(); +} + +NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const +{ + ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); + return operation; +} + +NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const +{ + return new ConvertRGBToYUVOperation(); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h new file mode 100644 index 00000000000..10b33039c86 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 Blender Foundation. */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +class SeparateColorNodeLegacy : public Node { + public: + SeparateColorNodeLegacy(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; + + protected: + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; +}; + +class SeparateRGBANode : public SeparateColorNodeLegacy { + public: + SeparateRGBANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateHSVANode : public SeparateColorNodeLegacy { + public: + SeparateHSVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYCCANode : public SeparateColorNodeLegacy { + public: + SeparateYCCANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +class SeparateYUVANode : public SeparateColorNodeLegacy { + public: + SeparateYUVANode(bNode *editor_node) : SeparateColorNodeLegacy(editor_node) + { + } + + NodeOperation *get_color_converter(const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 7579abf792a..24c0c577ac7 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -464,6 +464,68 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<floa } } +/* ******** RGB to HSL ******** */ + +ConvertRGBToHSLOperation::ConvertRGBToHSLOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertRGBToHSLOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_hsl_v(input_color, output); + output[3] = input_color[3]; +} + +void ConvertRGBToHSLOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + rgb_to_hsl_v(in, it.out); + it.out[3] = in[3]; + } +} + +/* ******** HSL to RGB ******** */ + +ConvertHSLToRGBOperation::ConvertHSLToRGBOperation() : ConvertBaseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); +} + +void ConvertHSLToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + hsl_to_rgb_v(input_color, output); + output[0] = max_ff(output[0], 0.0f); + output[1] = max_ff(output[1], 0.0f); + output[2] = max_ff(output[2], 0.0f); + output[3] = input_color[3]; +} + +void ConvertHSLToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it) +{ + for (; !it.is_end(); ++it) { + const float *in = it.in(0); + hsl_to_rgb_v(in, it.out); + it.out[0] = max_ff(it.out[0], 0.0f); + it.out[1] = max_ff(it.out[1], 0.0f); + it.out[2] = max_ff(it.out[2], 0.0f); + it.out[3] = in[3]; + } +} + /* ******** Premul to Straight ******** */ ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation() diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index e1904d61d46..16d1e2e6bb5 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -172,6 +172,26 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { void update_memory_buffer_partial(BuffersIterator<float> &it) override; }; +class ConvertRGBToHSLOperation : public ConvertBaseOperation { + public: + ConvertRGBToHSLOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + +class ConvertHSLToRGBOperation : public ConvertBaseOperation { + public: + ConvertHSLToRGBOperation(); + + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_partial(BuffersIterator<float> &it) override; +}; + class ConvertPremulToStraightOperation : public ConvertBaseOperation { public: ConvertPremulToStraightOperation(); diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 573a740dac8..725751d15af 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -112,7 +112,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, double *X, *Y, *W; const unsigned int src_width = src->get_width(); const unsigned int src_height = src->get_height(); - unsigned int x, y, sz; + unsigned int x, y, src_dim_max; unsigned int i; float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); @@ -202,10 +202,10 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, (void)0 /* Intermediate buffers. */ - sz = MAX2(src_width, src_height); - X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf"); - Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf"); - W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf"); + src_dim_max = MAX2(src_width, src_height); + X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf"); + Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf"); + W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf"); if (xy & 1) { /* H. */ int offset; for (y = 0; y < src_height; y++) { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index aeaf6b659e3..341541b4cdd 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -325,7 +325,6 @@ void OutputStereoOperation::deinit_execution() /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, &format_); - IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 372e0736cd2..49de275c256 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -219,6 +219,12 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, image_input_ = nullptr; BKE_image_format_init_for_write(&format_, scene, format); + if (!save_as_render) { + /* If not saving as render, stop IMB_colormanagement_imbuf_for_write using this + * colorspace for conversion. */ + format_.linear_colorspace_settings.name[0] = '\0'; + } + BLI_strncpy(path_, path, sizeof(path_)); view_name_ = view_name; diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index a5693cb0fd7..3d539018cef 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC intern/builder/deg_builder_relations_view_layer.cc intern/builder/deg_builder_remove_noop.cc intern/builder/deg_builder_rna.cc + intern/builder/deg_builder_stack.cc intern/builder/deg_builder_transitive.cc intern/builder/pipeline.cc intern/builder/pipeline_all_objects.cc @@ -103,6 +104,7 @@ set(SRC intern/builder/deg_builder_relations_impl.h intern/builder/deg_builder_remove_noop.h intern/builder/deg_builder_rna.h + intern/builder/deg_builder_stack.h intern/builder/deg_builder_transitive.h intern/builder/pipeline.h intern/builder/pipeline_all_objects.h diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index ade75fa2f6f..12663e74d24 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -64,7 +64,7 @@ bool DEG_id_type_any_updated(const struct Depsgraph *depsgraph); bool DEG_id_type_any_exists(const struct Depsgraph *depsgraph, short id_type); /** Get additional evaluation flags for the given ID. */ -uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, struct ID *id); +uint32_t DEG_get_eval_flags_for_id(const struct Depsgraph *graph, const struct ID *id); /** Get additional mesh CustomData_MeshMasks flags for the given object. */ void DEG_get_customdata_mask_for_object(const struct Depsgraph *graph, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index c6fc3cd5d0b..657bc3eb25c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -78,6 +78,7 @@ #include "BKE_modifier.h" #include "BKE_movieclip.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" @@ -1083,7 +1084,8 @@ void DepsgraphNodeBuilder::build_animation_images(ID *id) bool has_image_animation = false; if (ELEM(GS(id->name), ID_MA, ID_WO)) { bNodeTree *ntree = *BKE_ntree_ptr_from_id(id); - if (ntree != nullptr && ntree->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) { + if (ntree != nullptr && + ntree->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) { has_image_animation = true; } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 3eeab23823c..ae159373efd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -71,6 +71,7 @@ #include "BKE_mball.h" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" @@ -586,11 +587,12 @@ void DepsgraphRelationBuilder::build_id(ID *id) void DepsgraphRelationBuilder::build_generic_id(ID *id) { - if (built_map_.checkIsBuiltAndTag(id)) { return; } + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*id); + build_idproperties(id->properties); build_animdata(id); build_parameters(id); @@ -621,6 +623,9 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll * recurses into all the nested objects and collections. */ return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(collection->id); + const bool group_done = built_map_.checkIsBuiltAndTag(collection); OperationKey object_transform_final_key(object != nullptr ? &object->id : nullptr, NodeType::TRANSFORM, @@ -684,6 +689,9 @@ void DepsgraphRelationBuilder::build_object(Object *object) if (built_map_.checkIsBuiltAndTag(object)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(object->id); + /* Object Transforms */ OperationCode base_op = (object->parent) ? OperationCode::TRANSFORM_PARENT : OperationCode::TRANSFORM_LOCAL; @@ -1133,6 +1141,9 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, if (cti == nullptr) { continue; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*con); + /* Special case for camera tracking -- it doesn't use targets to * define relations. */ /* TODO: we can now represent dependencies in a much richer manner, @@ -1449,7 +1460,8 @@ void DepsgraphRelationBuilder::build_animation_images(ID *id) bool has_image_animation = false; if (ELEM(GS(id->name), ID_MA, ID_WO)) { bNodeTree *ntree = *BKE_ntree_ptr_from_id(id); - if (ntree != nullptr && ntree->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) { + if (ntree != nullptr && + ntree->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) { has_image_animation = true; } } @@ -1500,6 +1512,9 @@ void DepsgraphRelationBuilder::build_action(bAction *action) if (built_map_.checkIsBuiltAndTag(action)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(action->id); + build_idproperties(action->id.properties); if (!BLI_listbase_is_empty(&action->curves)) { TimeSourceKey time_src_key; @@ -1787,6 +1802,9 @@ void DepsgraphRelationBuilder::build_world(World *world) if (built_map_.checkIsBuiltAndTag(world)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(world->id); + build_idproperties(world->id.properties); /* animation */ build_animdata(&world->id); @@ -2012,6 +2030,9 @@ void DepsgraphRelationBuilder::build_particle_settings(ParticleSettings *part) if (built_map_.checkIsBuiltAndTag(part)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(part->id); + /* Animation data relations. */ build_animdata(&part->id); build_parameters(&part->id); @@ -2070,6 +2091,9 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key) if (built_map_.checkIsBuiltAndTag(key)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(key->id); + build_idproperties(key->id.properties); /* Attach animdata to geometry. */ build_animdata(&key->id); @@ -2131,6 +2155,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->updateDepsgraph) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md); + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); @@ -2251,6 +2277,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) if (built_map_.checkIsBuiltAndTag(obdata)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*obdata); + build_idproperties(obdata->properties); /* Animation. */ build_animdata(obdata); @@ -2369,6 +2398,9 @@ void DepsgraphRelationBuilder::build_armature(bArmature *armature) if (built_map_.checkIsBuiltAndTag(armature)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(armature->id); + build_idproperties(armature->id.properties); build_animdata(&armature->id); build_parameters(&armature->id); @@ -2388,6 +2420,9 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) if (built_map_.checkIsBuiltAndTag(camera)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(camera->id); + build_idproperties(camera->id.properties); build_animdata(&camera->id); build_parameters(&camera->id); @@ -2405,6 +2440,9 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) if (built_map_.checkIsBuiltAndTag(lamp)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(lamp->id); + build_idproperties(lamp->id.properties); build_animdata(&lamp->id); build_parameters(&lamp->id); @@ -2469,6 +2507,9 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(ntree->id); + build_idproperties(ntree->id.properties); build_animdata(&ntree->id); build_parameters(&ntree->id); @@ -2574,6 +2615,9 @@ void DepsgraphRelationBuilder::build_material(Material *material) if (built_map_.checkIsBuiltAndTag(material)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(material->id); + build_idproperties(material->id.properties); /* animation */ build_animdata(&material->id); @@ -2610,6 +2654,9 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) if (built_map_.checkIsBuiltAndTag(texture)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(texture->id); + /* texture itself */ ComponentKey texture_key(&texture->id, NodeType::GENERIC_DATABLOCK); build_idproperties(texture->id.properties); @@ -2651,6 +2698,9 @@ void DepsgraphRelationBuilder::build_image(Image *image) if (built_map_.checkIsBuiltAndTag(image)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(image->id); + build_idproperties(image->id.properties); build_parameters(&image->id); } @@ -2660,6 +2710,9 @@ void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file) if (built_map_.checkIsBuiltAndTag(cache_file)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(cache_file->id); + build_idproperties(cache_file->id.properties); /* Animation. */ build_animdata(&cache_file->id); @@ -2689,6 +2742,9 @@ void DepsgraphRelationBuilder::build_mask(Mask *mask) if (built_map_.checkIsBuiltAndTag(mask)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(mask->id); + ID *mask_id = &mask->id; build_idproperties(mask_id->properties); /* F-Curve animation. */ @@ -2727,6 +2783,8 @@ void DepsgraphRelationBuilder::build_freestyle_linestyle(FreestyleLineStyle *lin return; } + const BuilderStack::ScopedEntry stack_entry = stack_.trace(linestyle->id); + ID *linestyle_id = &linestyle->id; build_parameters(linestyle_id); build_idproperties(linestyle_id->properties); @@ -2739,6 +2797,9 @@ void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) if (built_map_.checkIsBuiltAndTag(clip)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(clip->id); + /* Animation. */ build_idproperties(clip->id.properties); build_animdata(&clip->id); @@ -2750,6 +2811,9 @@ void DepsgraphRelationBuilder::build_lightprobe(LightProbe *probe) if (built_map_.checkIsBuiltAndTag(probe)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(probe->id); + build_idproperties(probe->id.properties); build_animdata(&probe->id); build_parameters(&probe->id); @@ -2760,6 +2824,9 @@ void DepsgraphRelationBuilder::build_speaker(Speaker *speaker) if (built_map_.checkIsBuiltAndTag(speaker)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(speaker->id); + build_idproperties(speaker->id.properties); build_animdata(&speaker->id); build_parameters(&speaker->id); @@ -2776,6 +2843,9 @@ void DepsgraphRelationBuilder::build_sound(bSound *sound) if (built_map_.checkIsBuiltAndTag(sound)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(sound->id); + build_idproperties(sound->id.properties); build_animdata(&sound->id); build_parameters(&sound->id); @@ -2786,6 +2856,9 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation) if (built_map_.checkIsBuiltAndTag(simulation)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(simulation->id); + build_idproperties(simulation->id.properties); build_animdata(&simulation->id); build_parameters(&simulation->id); @@ -2850,6 +2923,9 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_SCENE_SEQUENCER)) { return; } + + /* TODO(sergey): Trace as a scene sequencer. */ + build_scene_audio(scene); ComponentKey scene_audio_key(&scene->id, NodeType::AUDIO); /* Make sure dependencies from sequences data goes to the sequencer evaluation. */ @@ -2893,6 +2969,9 @@ void DepsgraphRelationBuilder::build_vfont(VFont *vfont) if (built_map_.checkIsBuiltAndTag(vfont)) { return; } + + const BuilderStack::ScopedEntry stack_entry = stack_.trace(vfont->id); + build_parameters(&vfont->id); build_idproperties(vfont->id.properties); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 1ccecc9a3f2..64bdd2334d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -23,6 +23,7 @@ #include "intern/builder/deg_builder.h" #include "intern/builder/deg_builder_map.h" #include "intern/builder/deg_builder_rna.h" +#include "intern/builder/deg_builder_stack.h" #include "intern/depsgraph.h" #include "intern/node/deg_node.h" #include "intern/node/deg_node_component.h" @@ -363,6 +364,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { BuilderMap built_map_; RNANodeQuery rna_node_query_; + BuilderStack stack_; }; struct DepsNodeHandle { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index 5cbd8c8dd75..aba4a011e72 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -9,6 +9,8 @@ #include "intern/node/deg_node_id.h" +#include <iostream> + #include "DNA_ID.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" @@ -33,37 +35,30 @@ Relation *DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from, Node *node_to = get_node(key_to); OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr; OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr; + if (op_from && op_to) { return add_operation_relation(op_from, op_to, description, flags); } - else { - if (!op_from) { - /* XXX TODO: handle as error or report if needed. */ - fprintf(stderr, - "add_relation(%s) - Could not find op_from (%s)\n", - description, - key_from.identifier().c_str()); - } - else { - fprintf(stderr, - "add_relation(%s) - Failed, but op_from (%s) was ok\n", - description, - key_from.identifier().c_str()); - } - if (!op_to) { - /* XXX TODO: handle as error or report if needed. */ - fprintf(stderr, - "add_relation(%s) - Could not find op_to (%s)\n", - description, - key_to.identifier().c_str()); - } - else { - fprintf(stderr, - "add_relation(%s) - Failed, but op_to (%s) was ok\n", - description, - key_to.identifier().c_str()); - } + + /* TODO(sergey): Report error in the interface. */ + + std::cerr << "--------------------------------------------------------------------\n"; + std::cerr << "Failed to add relation \"" << description << "\"\n"; + + if (!op_from) { + std::cerr << "Could not find op_from: " << key_from.identifier() << "\n"; + } + + if (!op_to) { + std::cerr << "Could not find op_to: " << key_to.identifier() << "\n"; } + + if (!stack_.is_empty()) { + std::cerr << "\nTrace:\n\n"; + stack_.print_backtrace(std::cerr); + std::cerr << "\n"; + } + return nullptr; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index 65cf0e7d9df..2e491cd37a6 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -322,7 +322,11 @@ void DepsgraphRelationBuilder::build_rig(Object *object) RootPChanMap root_map; bool pose_depends_on_local_transform = false; LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*pchan); + LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*con); + switch (con->type) { case CONSTRAINT_TYPE_KINEMATIC: build_ik_pose(object, pchan, con, &root_map); @@ -356,6 +360,8 @@ void DepsgraphRelationBuilder::build_rig(Object *object) } /* Links between operations for each bone. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*pchan); + build_idproperties(pchan->prop); OperationKey bone_local_key( &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index cdb7361afc0..cd1917cb607 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -36,6 +36,9 @@ void DepsgraphRelationBuilder::build_scene_parameters(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_PARAMETERS)) { return; } + + /* TODO(sergey): Trace as a scene parameters. */ + build_idproperties(scene->id.properties); build_parameters(&scene->id); OperationKey parameters_eval_key( @@ -56,6 +59,9 @@ void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene) if (scene->nodetree == nullptr) { return; } + + /* TODO(sergey): Trace as a scene compositor. */ + build_nodetree(scene->nodetree); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 8a81adf0aeb..ac7a5bc2f30 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -39,7 +39,7 @@ namespace blender::deg { class RNANodeQueryIDData { public: - explicit RNANodeQueryIDData(const ID *id) : id_(id), constraint_to_pchan_map_(nullptr) + explicit RNANodeQueryIDData(const ID *id) : id_(id) { } @@ -77,7 +77,7 @@ class RNANodeQueryIDData { /* indexed by bConstraint*, returns pose channel which contains that * constraint. */ - Map<const bConstraint *, const bPoseChannel *> *constraint_to_pchan_map_; + Map<const bConstraint *, const bPoseChannel *> *constraint_to_pchan_map_ = nullptr; }; /* ***************************** Node Identifier **************************** */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_stack.cc b/source/blender/depsgraph/intern/builder/deg_builder_stack.cc new file mode 100644 index 00000000000..de0a5198a8a --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_stack.cc @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup depsgraph + */ + +#include "intern/builder/deg_builder_stack.h" + +#include <iomanip> +#include <ios> +#include <iostream> + +#include "BKE_idtype.h" + +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" + +namespace blender::deg { + +/* Spacing between adjacent columns, in number of spaces. */ +constexpr int kColumnSpacing = 4; + +/* Width of table columns including column padding. + * The type column width is a guesstimate based on "Particle Settings" with some extra padding. */ +constexpr int kPrintDepthWidth = 5 + kColumnSpacing; +constexpr int kPrintTypeWidth = 21 + kColumnSpacing; + +namespace { + +/* NOTE: Depth column printing is already taken care of. */ + +void print(std::ostream &stream, const ID *id) +{ + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); + stream << std::setw(kPrintTypeWidth) << id_type_info->name << (id->name + 2) << "\n"; +} + +void print(std::ostream &stream, const bConstraint *constraint) +{ + stream << std::setw(kPrintTypeWidth) << ("Constraint") << constraint->name << "\n"; +} + +void print(std::ostream &stream, const ModifierData *modifier_data) +{ + stream << std::setw(kPrintTypeWidth) << ("Modifier") << modifier_data->name << "\n"; +} + +void print(std::ostream &stream, const bPoseChannel *pchan) +{ + stream << std::setw(kPrintTypeWidth) << ("Pose Channel") << pchan->name << "\n"; +} + +} // namespace + +void BuilderStack::print_backtrace(std::ostream &stream) +{ + const std::ios_base::fmtflags old_flags(stream.flags()); + + stream << std::left; + + stream << std::setw(kPrintDepthWidth) << "Depth" << std::setw(kPrintTypeWidth) << "Type" + << "Name" + << "\n"; + + stream << std::setw(kPrintDepthWidth) << "-----" << std::setw(kPrintTypeWidth) << "----" + << "----" + << "\n"; + + int depth = 1; + for (const Entry &entry : stack_) { + stream << std::setw(kPrintDepthWidth) << depth; + ++depth; + + if (entry.id_ != nullptr) { + print(stream, entry.id_); + } + else if (entry.constraint_ != nullptr) { + print(stream, entry.constraint_); + } + else if (entry.modifier_data_ != nullptr) { + print(stream, entry.modifier_data_); + } + else if (entry.pchan_ != nullptr) { + print(stream, entry.pchan_); + } + } + + stream.flags(old_flags); +} + +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_stack.h b/source/blender/depsgraph/intern/builder/deg_builder_stack.h new file mode 100644 index 00000000000..3f9cc83928a --- /dev/null +++ b/source/blender/depsgraph/intern/builder/deg_builder_stack.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup depsgraph + */ + +#pragma once + +#include "BLI_utildefines.h" +#include "BLI_vector.hh" + +struct ID; +struct bConstraint; +struct bPoseChannel; +struct ModifierData; + +namespace blender::deg { + +/* This class keeps track of the builder calls nesting, allowing to unroll them back and provide a + * clue about how the builder made it to its current state. + * + * The tracing is based on the builder giving a trace clues to the stack. Typical usage is: + * + * void DepsgraphRelationBuilder::my_id_builder(ID *id) + * { + * if (built_map_.checkIsBuiltAndTag(id)) { + * return; + * } + * + * const BuilderStack::ScopedEntry stack_entry = stack_.trace(*id); + * + * ... + * } + */ +class BuilderStack { + public: + /* Entry of the backtrace. + * A cheap-to-construct wrapper which allows to gather a proper string representation whenever + * the stack is printed. */ + class Entry { + public: + explicit Entry(const ID &id) : id_(&id) + { + } + + explicit Entry(const bConstraint &constraint) : constraint_(&constraint) + { + } + + explicit Entry(const bPoseChannel &pchan) : pchan_(&pchan) + { + } + + explicit Entry(const ModifierData &modifier_data) : modifier_data_(&modifier_data) + { + } + + private: + friend class BuilderStack; + + const ID *id_ = nullptr; + const bConstraint *constraint_ = nullptr; + const ModifierData *modifier_data_ = nullptr; + const bPoseChannel *pchan_ = nullptr; + }; + + using Stack = Vector<Entry>; + + /* A helper class to provide a RAII style of tracing. It is constructed by the + * `BuilderStack::trace` (which pushes entry to the stack), and upon destruction of this object + * the corresponding entry is popped from the stack. + * + * The goal of this `ScopedEntry` is to free developers from worrying about removing entries from + * the stack whenever leaving a builder step scope. */ + class ScopedEntry { + public: + /* Delete copy constructor and operator: scoped entries are only supposed to be constructed + * once and never copied. */ + ScopedEntry(const ScopedEntry &other) = delete; + ScopedEntry &operator=(const ScopedEntry &other) = delete; + + /* Move semantic. */ + ScopedEntry(ScopedEntry &&other) noexcept : stack_(other.stack_) + { + other.stack_ = nullptr; + } + ScopedEntry &operator=(ScopedEntry &&other) + { + if (this == &other) { + return *this; + } + + stack_ = other.stack_; + other.stack_ = nullptr; + + return *this; + } + + ~ScopedEntry() + { + /* Stack will become nullptr when the entry was moved somewhere else. */ + if (stack_ != nullptr) { + BLI_assert(!stack_->is_empty()); + stack_->pop_last(); + } + } + + private: + friend BuilderStack; + + explicit ScopedEntry(Stack &stack) : stack_(&stack) + { + } + + Stack *stack_; + }; + + BuilderStack() = default; + ~BuilderStack() = default; + + bool is_empty() const + { + return stack_.is_empty(); + } + + void print_backtrace(std::ostream &stream); + + template<class... Args> ScopedEntry trace(const Args &...args) + { + stack_.append_as(args...); + + return ScopedEntry(stack_); + } + + private: + Stack stack_; +}; + +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index fd569599b8b..6ffc711a475 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -32,6 +32,49 @@ #include "intern/eval/deg_eval_copy_on_write.h" #include "intern/node/deg_node_id.h" +namespace blender::deg { + +static const ID *get_original_id(const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + if (id->orig_id == nullptr) { + return id; + } + BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); + return (ID *)id->orig_id; +} + +static ID *get_original_id(ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_original_id(const_id)); +} + +static const ID *get_evaluated_id(const Depsgraph *deg_graph, const ID *id) +{ + if (id == nullptr) { + return nullptr; + } + /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), + * but here we never do assert, since we don't know nature of the + * incoming ID data-block. */ + const IDNode *id_node = deg_graph->find_id_node(id); + if (id_node == nullptr) { + return id; + } + return id_node->id_cow; +} + +static ID *get_evaluated_id(const Depsgraph *deg_graph, ID *id) +{ + const ID *const_id = id; + return const_cast<ID *>(get_evaluated_id(deg_graph, const_id)); +} + +} // namespace blender::deg + namespace deg = blender::deg; struct Scene *DEG_get_input_scene(const Depsgraph *graph) @@ -90,7 +133,7 @@ bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type) return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0; } -uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) +uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id) { if (graph == nullptr) { /* Happens when converting objects to mesh from a python script @@ -102,7 +145,7 @@ uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, ID *id) } const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph); - const deg::IDNode *id_node = deg_graph->find_id_node(DEG_get_original_id(id)); + const deg::IDNode *id_node = deg_graph->find_id_node(deg::get_original_id(id)); if (id_node == nullptr) { /* TODO(sergey): Does it mean we need to check set scene? */ return 0; @@ -171,18 +214,7 @@ Object *DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object) ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id) { - if (id == nullptr) { - return nullptr; - } - /* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(), - * but here we never do assert, since we don't know nature of the - * incoming ID data-block. */ - const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph; - const deg::IDNode *id_node = deg_graph->find_id_node(id); - if (id_node == nullptr) { - return id; - } - return id_node->id_cow; + return deg::get_evaluated_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id); } void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph, @@ -249,14 +281,7 @@ Object *DEG_get_original_object(Object *object) ID *DEG_get_original_id(ID *id) { - if (id == nullptr) { - return nullptr; - } - if (id->orig_id == nullptr) { - return id; - } - BLI_assert((id->tag & LIB_TAG_COPIED_ON_WRITE) != 0); - return (ID *)id->orig_id; + return deg::get_original_id(id); } bool DEG_is_original_id(const ID *id) diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 1ff7585165b..d2a89576022 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/mesh_extractors/extract_mesh_vbo_uv.cc intern/mesh_extractors/extract_mesh_vbo_vcol.cc intern/mesh_extractors/extract_mesh_vbo_weights.cc + intern/draw_attributes.cc intern/draw_cache_impl_curve.cc intern/draw_cache_impl_curves.cc intern/draw_cache_impl_displist.c @@ -85,7 +86,7 @@ set(SRC intern/draw_curves.cc intern/draw_debug.c intern/draw_fluid.c - intern/draw_hair.c + intern/draw_hair.cc intern/draw_instance_data.c intern/draw_manager.c intern/draw_manager_data.c @@ -95,7 +96,7 @@ set(SRC intern/draw_manager_text.c intern/draw_manager_texture.c intern/draw_select_buffer.c - intern/draw_shader.c + intern/draw_shader.cc intern/draw_texture_pool.cc intern/draw_view.c intern/draw_view_data.cc @@ -133,12 +134,14 @@ set(SRC engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c + engines/eevee_next/eevee_camera.cc engines/eevee_next/eevee_engine.cc engines/eevee_next/eevee_instance.cc engines/eevee_next/eevee_material.cc engines/eevee_next/eevee_pipeline.cc engines/eevee_next/eevee_shader.cc engines/eevee_next/eevee_sync.cc + engines/eevee_next/eevee_velocity.cc engines/eevee_next/eevee_view.cc engines/eevee_next/eevee_world.cc engines/workbench/workbench_data.c @@ -196,6 +199,7 @@ set(SRC DRW_select_buffer.h intern/DRW_gpu_wrapper.hh intern/DRW_render.h + intern/draw_attributes.h intern/draw_cache.h intern/draw_cache_extract.h intern/draw_cache_impl.h @@ -352,6 +356,7 @@ set(GLSL_SRC engines/eevee/shaders/world_vert.glsl engines/eevee_next/shaders/eevee_attributes_lib.glsl + engines/eevee_next/shaders/eevee_camera_lib.glsl engines/eevee_next/shaders/eevee_geom_curves_vert.glsl engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -362,6 +367,11 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_surf_forward_frag.glsl engines/eevee_next/shaders/eevee_surf_lib.glsl engines/eevee_next/shaders/eevee_surf_world_frag.glsl + engines/eevee_next/shaders/eevee_velocity_lib.glsl + engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl + + engines/eevee_next/eevee_defines.hh + engines/eevee_next/eevee_shader_shared.hh engines/workbench/shaders/workbench_cavity_lib.glsl engines/workbench/shaders/workbench_common_lib.glsl @@ -422,8 +432,8 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl - intern/draw_shader_shared.h intern/draw_common_shader_shared.h + intern/draw_shader_shared.h engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h index 324ebebfbe6..5bc7aee92ba 100644 --- a/source/blender/draw/DRW_select_buffer.h +++ b/source/blender/draw/DRW_select_buffer.h @@ -9,6 +9,10 @@ #include "BLI_sys_types.h" /* for bool and uint */ +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct Base; struct Depsgraph; @@ -133,3 +137,7 @@ uint DRW_select_buffer_find_nearest_to_point(struct Depsgraph *depsgraph, uint id_max, uint *dist); void DRW_select_buffer_context_create(struct Base **bases, uint bases_len, short select_mode); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index b17efe4b68d..33063e14c03 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -68,7 +68,7 @@ BLI_INLINE int eevee_cryptomatte_layers_count(const ViewLayer *view_layer) } /* The number of render result passes are needed to store a single cryptomatte layer. Per - * renderpass 2 cryptomatte samples can be stored. */ + * render-pass 2 cryptomatte samples can be stored. */ BLI_INLINE int eevee_cryptomatte_passes_per_layer(const ViewLayer *view_layer) { const int num_cryptomatte_levels = view_layer->cryptomatte_levels; diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 4f562dd9804..7f722ff1764 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -95,7 +95,7 @@ typedef struct EEVEE_LightBake { /** Target layer to store the data to. */ int layer; /** Sample count for the convolution. */ - float samples_ct, invsamples_ct; + float samples_count, invsamples_count; /** Sampling bias during convolution step. */ float lod_factor; /** Max cube-map LOD to sample when convolving. */ @@ -282,14 +282,14 @@ static void irradiance_pool_size_get(int visibility_size, int total_samples, int (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); /* The irradiance itself take one layer, hence the +1 */ - int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + int layer_count = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); - int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + int texel_count = (int)ceilf((float)total_samples / (float)(layer_count - 1)); r_size[0] = visibility_size * - max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + max_ii(1, min_ii(texel_count, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); r_size[1] = visibility_size * - max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_ct; + max_ii(1, (texel_count / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_count; } static bool EEVEE_lightcache_validate(const LightCache *light_cache, @@ -1118,7 +1118,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); /* TODO: do this once for the whole bake when we have independent DRWManagers. - * Warning: Some of the things above require this. */ + * WARNING: Some of the things above require this. */ eevee_lightbake_cache_create(vedata, lbake); /* Compute sample position */ diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 43d0b050cc8..a4bd789438d 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -112,7 +112,7 @@ void EEVEE_lookdev_init(EEVEE_Data *vedata) if (sphere_size != effects->sphere_size || rect->xmax != effects->anchor[0] || rect->ymin != effects->anchor[1]) { - /* Make sphere resolution adaptive to viewport_scale, dpi and lookdev_sphere_size */ + /* Make sphere resolution adaptive to viewport_scale, DPI and #U.lookdev_sphere_size. */ float res_scale = clamp_f( (U.lookdev_sphere_size / 400.0f) * viewport_scale * U.dpi_fac, 0.1f, 1.0f); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index b03a4fa70b4..0a7c8e185c4 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -301,7 +301,7 @@ typedef struct EEVEE_PassList { struct DRWPass *maxz_copydepth_ps; struct DRWPass *maxz_copydepth_layer_ps; - /* Renderpass Accumulation. */ + /* Render-pass Accumulation. */ struct DRWPass *material_accum_ps; struct DRWPass *background_accum_ps; struct DRWPass *cryptomatte_ps; @@ -1069,7 +1069,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; GPUTexture *renderpass_transmittance_input; - /* Renderpass ubo reference used by material pass. */ + /* Render-pass UBO reference used by material pass. */ struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc index 05577944140..216a15de2b9 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc +++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc @@ -118,6 +118,10 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, info.vertex_inputs_.clear(); } + if (is_hair) { + info.additional_info("draw_curves_infos"); + } + if (!is_volume) { info.define("EEVEE_GENERATED_INTERFACE"); info.vertex_out(*stage_interface); diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index 21d347942ca..4070ede116b 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -24,7 +24,7 @@ struct Closure { float holdout; #endif -/* Metal Default Constructor - Requred for C++ constructor syntax. */ +/* Metal Default Constructor - Required for C++ constructor syntax. */ #ifdef GPU_METAL inline Closure() = default; # ifdef VOLUMETRICS diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 5295a05b965..2926f8c5a89 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -65,6 +65,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported. */ @@ -73,22 +89,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 81f73979723..8e1bafe8d92 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -201,4 +201,4 @@ vec3 coordinate_incoming(vec3 P) #else return cameraVec(P); #endif -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 04f38978076..a8e95e13b12 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -72,6 +72,22 @@ vec3 attr_load_orco(samplerBuffer cd_buf) } # endif +/* Per attribute scope follows loading order. */ +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = hairStrandID; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { return vec4(hairTangent, 1.0); @@ -79,22 +95,22 @@ vec4 attr_load_tangent(samplerBuffer cd_buf) vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, hairStrandID).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } #else diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index d6eeedd8640..88ade8451a4 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -8,7 +8,7 @@ flat in int slice; -/* Warning: these are not attributes, these are global vars. */ +/* WARNING: these are not attributes, these are global vars. */ vec3 worldPosition = vec3(0.0); vec3 objectPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); @@ -80,8 +80,8 @@ void main() volumeOrco = OrcoTexCoFactors[0].xyz + objectPosition * OrcoTexCoFactors[1].xyz; if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) { - /* Note: Discard is not an explicit return in Metal prior to versions 2.3. - * adding return after discard ensures consistent behaviour and avoids GPU + /* NOTE: Discard is not an explicit return in Metal prior to versions 2.3. + * adding return after discard ensures consistent behavior and avoids GPU * side-effects where control flow continues with undefined values. */ discard; return; @@ -157,7 +157,7 @@ float attr_load_float(sampler3D tex) } /* TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on - * the engine side. But as of now, the engines are reponsible for loading the attributes. */ + * the engine side. But as of now, the engines are responsible for loading the attributes. */ float attr_load_temperature_post(float attr) { #ifdef MESH_SHADER diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl index 527bbd18896..3ce54b3122a 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -70,7 +70,7 @@ void main() vec3 Tr = exp(-s_extinction * s_len); /* integrate along the current step segment */ - /* Note: Original calculation carries precision issues when compiling for AMD GPUs + /* NOTE: Original calculation carries precision issues when compiling for AMD GPUs * and running Metal. This version of the equation retains precision well for all * macOS HW configurations. */ Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction); diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl index ce863bdf660..186f438b03b 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl @@ -76,4 +76,4 @@ vec3 coordinate_reflect(vec3 P, vec3 N) vec3 coordinate_incoming(vec3 P) { return vec3(0.0); -}
\ No newline at end of file +} diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc new file mode 100644 index 00000000000..e6d2e2db764 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +#include <array> + +#include "DRW_render.h" + +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_camera.h" +#include "DEG_depsgraph_query.h" +#include "RE_pipeline.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +void Camera::init() +{ + const Object *camera_eval = inst_.camera_eval_object; + synced_ = false; + data_.swap(); + + CameraData &data = data_.current(); + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + switch (cam->type) { + default: + case CAM_PERSP: + data.type = CAMERA_PERSP; + break; + case CAM_ORTHO: + data.type = CAMERA_ORTHO; + break; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + case CAM_PANO: { + switch (cam->panorama_type) { + default: + case CAM_PANO_EQUIRECTANGULAR: + data.type = CAMERA_PANO_EQUIRECT; + break; + case CAM_PANO_FISHEYE_EQUIDISTANT: + data.type = CAMERA_PANO_EQUIDISTANT; + break; + case CAM_PANO_FISHEYE_EQUISOLID: + data.type = CAMERA_PANO_EQUISOLID; + break; + case CAM_PANO_MIRRORBALL: + data.type = CAMERA_PANO_MIRROR; + break; + } + } +#endif + } + } + else if (inst_.drw_view) { + data.type = DRW_view_is_persp_get(inst_.drw_view) ? CAMERA_PERSP : CAMERA_ORTHO; + } + else { + /* Light-probe baking. */ + data.type = CAMERA_PERSP; + } +} + +void Camera::sync() +{ + const Object *camera_eval = inst_.camera_eval_object; + CameraData &data = data_.current(); + + data.filter_size = inst_.scene->r.gauss; + + if (inst_.drw_view) { + DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false); + DRW_view_viewmat_get(inst_.drw_view, data.viewinv.ptr(), true); + DRW_view_winmat_get(inst_.drw_view, data.winmat.ptr(), false); + DRW_view_winmat_get(inst_.drw_view, data.wininv.ptr(), true); + DRW_view_persmat_get(inst_.drw_view, data.persmat.ptr(), false); + DRW_view_persmat_get(inst_.drw_view, data.persinv.ptr(), true); + DRW_view_camtexco_get(inst_.drw_view, data.uv_scale); + } + else if (inst_.render) { + /* TODO(@fclem): Over-scan. */ + // RE_GetCameraWindowWithOverscan(inst_.render->re, g_data->overscan, data.winmat); + RE_GetCameraWindow(inst_.render->re, camera_eval, data.winmat.ptr()); + RE_GetCameraModelMatrix(inst_.render->re, camera_eval, data.viewinv.ptr()); + invert_m4_m4(data.viewmat.ptr(), data.viewinv.ptr()); + invert_m4_m4(data.wininv.ptr(), data.winmat.ptr()); + mul_m4_m4m4(data.persmat.ptr(), data.winmat.ptr(), data.viewmat.ptr()); + invert_m4_m4(data.persinv.ptr(), data.persmat.ptr()); + data.uv_scale = float2(1.0f); + data.uv_bias = float2(0.0f); + } + else { + data.viewmat = float4x4::identity(); + data.viewinv = float4x4::identity(); + perspective_m4(data.winmat.ptr(), -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f); + data.wininv = data.winmat.inverted(); + data.persmat = data.winmat * data.viewmat; + data.persinv = data.persmat.inverted(); + } + + if (camera_eval) { + const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data); + data.clip_near = cam->clip_start; + data.clip_far = cam->clip_end; +#if 0 /* TODO(fclem): Make fisheye properties inside blender. */ + data.fisheye_fov = cam->fisheye_fov; + data.fisheye_lens = cam->fisheye_lens; + data.equirect_bias.x = -cam->longitude_min + M_PI_2; + data.equirect_bias.y = -cam->latitude_min + M_PI_2; + data.equirect_scale.x = cam->longitude_min - cam->longitude_max; + data.equirect_scale.y = cam->latitude_min - cam->latitude_max; + /* Combine with uv_scale/bias to avoid doing extra computation. */ + data.equirect_bias += data.uv_bias * data.equirect_scale; + data.equirect_scale *= data.uv_scale; + + data.equirect_scale_inv = 1.0f / data.equirect_scale; +#endif + } + else if (inst_.drw_view) { + data.clip_near = DRW_view_near_distance_get(inst_.drw_view); + data.clip_far = DRW_view_far_distance_get(inst_.drw_view); + data.fisheye_fov = data.fisheye_lens = -1.0f; + data.equirect_bias = float2(0.0f); + data.equirect_scale = float2(0.0f); + } + + data_.current().push_update(); + + synced_ = true; + + /* Detect changes in parameters. */ + if (data_.current() != data_.previous()) { + // inst_.sampling.reset(); + } +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh index 3db343703e0..dfec738b1f3 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.hh +++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2021 Blender Foundation. - */ + * Copyright 2021 Blender Foundation. */ + +#pragma once /** \file * \ingroup eevee */ +#include "eevee_shader_shared.hh" + namespace blender::eevee { class Instance; @@ -43,4 +46,85 @@ static const float cubeface_mat[6][4][4] = { {0.0f, 0.0f, 0.0f, 1.0f}}, }; +inline void cubeface_winmat_get(float4x4 &winmat, float near, float far) +{ + /* Simple 90° FOV projection. */ + perspective_m4(winmat.ptr(), -near, near, -near, near, near, far); +} + +/* -------------------------------------------------------------------- */ +/** \name CameraData operators + * \{ */ + +inline bool operator==(const CameraData &a, const CameraData &b) +{ + return compare_m4m4(a.persmat.ptr(), b.persmat.ptr(), FLT_MIN) && (a.uv_scale == b.uv_scale) && + (a.uv_bias == b.uv_bias) && (a.equirect_scale == b.equirect_scale) && + (a.equirect_bias == b.equirect_bias) && (a.fisheye_fov == b.fisheye_fov) && + (a.fisheye_lens == b.fisheye_lens) && (a.filter_size == b.filter_size) && + (a.type == b.type); +} + +inline bool operator!=(const CameraData &a, const CameraData &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +/** + * Point of view in the scene. Can be init from viewport or camera object. + */ +class Camera { + private: + Instance &inst_; + + /** Double buffered to detect changes and have history for re-projection. */ + SwapChain<CameraDataBuf, 2> data_; + /** Detects wrong usage. */ + bool synced_ = false; + + public: + Camera(Instance &inst) : inst_(inst){}; + ~Camera(){}; + + void init(); + void sync(); + + /** + * Getters + **/ + const CameraData &data_get() const + { + BLI_assert(synced_); + return data_.current(); + } + const GPUUniformBuf *ubo_get() const + { + return data_.current(); + } + bool is_panoramic() const + { + return eevee::is_panoramic(data_.current().type); + } + bool is_orthographic() const + { + return data_.current().type == CAMERA_ORTHO; + } + const float3 &position() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]); + } + const float3 &forward() const + { + return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]); + } +}; + +/** \} */ + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 35eb33671db..7141928a20d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -11,9 +11,11 @@ #pragma once -/* Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. */ -/* Current limiting factor is the sorting phase which is single pass and only sort within a - * threadgroup which maximum size is 1024. */ +/** + Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536. + * Current limiting factor is the sorting phase which is single pass and only sort within a + * thread-group which maximum size is 1024. + */ #define CULLING_BATCH_SIZE 1024 /** diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc index a7d183c1c88..be0adfad568 100644 --- a/source/blender/draw/engines/eevee_next/eevee_engine.cc +++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc @@ -5,6 +5,7 @@ #include "BKE_global.h" #include "BLI_rect.h" +#include "GPU_capabilities.h" #include "GPU_framebuffer.h" #include "ED_view3d.h" @@ -24,10 +25,17 @@ struct EEVEE_Data { DRWViewportEmptyList *psl; DRWViewportEmptyList *stl; eevee::Instance *instance; + + char info[GPU_INFO_SIZE]; }; static void eevee_engine_init(void *vedata) { + /* TODO(fclem): Remove once it is minimum required. */ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata); if (ved->instance == nullptr) { ved->instance = new eevee::Instance(); @@ -81,31 +89,50 @@ static void eevee_engine_init(void *vedata) static void eevee_draw_scene(void *vedata) { + EEVEE_Data *ved = reinterpret_cast<EEVEE_Data *>(vedata); + if (!GPU_shader_storage_buffer_objects_support()) { + STRNCPY(ved->info, "Error: No shader storage buffer support"); + return; + } DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - reinterpret_cast<EEVEE_Data *>(vedata)->instance->draw_viewport(dfbl); + ved->instance->draw_viewport(dfbl); + STRNCPY(ved->info, ved->instance->info.c_str()); } static void eevee_cache_init(void *vedata) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->begin_sync(); } static void eevee_cache_populate(void *vedata, Object *object) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->object_sync(object); } static void eevee_cache_finish(void *vedata) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } reinterpret_cast<EEVEE_Data *>(vedata)->instance->end_sync(); } static void eevee_engine_free() { + eevee::ShaderModule::module_free(); } static void eevee_instance_free(void *instance) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } delete reinterpret_cast<eevee::Instance *>(instance); } @@ -114,11 +141,17 @@ static void eevee_render_to_image(void *UNUSED(vedata), struct RenderLayer *layer, const struct rcti *UNUSED(rect)) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } UNUSED_VARS(engine, layer); } static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) { + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } UNUSED_VARS(engine, scene, view_layer); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 922f6c9e1ae..606630bcdef 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -8,6 +8,8 @@ * An instance contains all structures needed to do a complete render. */ +#include <sstream> + #include "BKE_global.h" #include "BKE_object.h" #include "BLI_rect.h" @@ -21,9 +23,9 @@ namespace blender::eevee { /* -------------------------------------------------------------------- */ -/** \name Init +/** \name Initialization * - * Init funcions need to be called once at the start of a frame. + * Initialization functions need to be called once at the start of a frame. * Active camera, render extent and enabled render passes are immutable until next init. * This takes care of resizing output buffers and view in case a parameter changed. * IMPORTANT: xxx.init() functions are NOT meant to acquire and allocate DRW resources. @@ -41,26 +43,36 @@ void Instance::init(const int2 &output_res, const View3D *v3d_, const RegionView3D *rv3d_) { - UNUSED_VARS(light_probe_, camera_object_, output_rect); + UNUSED_VARS(light_probe_, output_rect); render = render_; depsgraph = depsgraph_; + camera_orig_object = camera_object_; render_layer = render_layer_; drw_view = drw_view_; v3d = v3d_; rv3d = rv3d_; + info = ""; + update_eval_members(); main_view.init(output_res); } +void Instance::set_time(float time) +{ + BLI_assert(render); + DRW_render_set_time(render, depsgraph, floorf(time), fractf(time)); + update_eval_members(); +} + void Instance::update_eval_members() { scene = DEG_get_evaluated_scene(depsgraph); view_layer = DEG_get_evaluated_view_layer(depsgraph); - // camera_eval_object = (camera_orig_object) ? - // DEG_get_evaluated_object(depsgraph, camera_orig_object) : - // nullptr; + camera_eval_object = (camera_orig_object) ? + DEG_get_evaluated_object(depsgraph, camera_orig_object) : + nullptr; } /** \} */ @@ -76,6 +88,7 @@ void Instance::update_eval_members() void Instance::begin_sync() { materials.begin_sync(); + velocity.begin_sync(); pipelines.sync(); main_view.sync(); @@ -135,6 +148,7 @@ void Instance::object_sync(Object *ob) void Instance::end_sync() { + velocity.end_sync(); } void Instance::render_sync() @@ -171,6 +185,13 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl) { UNUSED_VARS(dfbl); render_sample(); + velocity.step_swap(); + + if (materials.queued_shaders_count > 0) { + std::stringstream ss; + ss << "Compiling Shaders " << materials.queued_shaders_count; + info = ss.str(); + } } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index c3cf08c8390..84be59fc5f0 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -15,6 +15,7 @@ #include "DNA_lightprobe_types.h" #include "DRW_render.h" +#include "eevee_camera.hh" #include "eevee_material.hh" #include "eevee_pipeline.hh" #include "eevee_shader.hh" @@ -29,11 +30,15 @@ namespace blender::eevee { * \brief A running instance of the engine. */ class Instance { + friend VelocityModule; + public: ShaderModule &shaders; SyncModule sync; MaterialModule materials; PipelineModule pipelines; + VelocityModule velocity; + Camera camera; MainView main_view; World world; @@ -42,6 +47,8 @@ class Instance { /** Evaluated IDs. */ Scene *scene; ViewLayer *view_layer; + Object *camera_eval_object; + Object *camera_orig_object; /** Only available when rendering for final render. */ const RenderLayer *render_layer; RenderEngine *render; @@ -51,7 +58,7 @@ class Instance { const RegionView3D *rv3d; /* Info string displayed at the top of the render / viewport. */ - char info[64]; + std::string info = ""; public: Instance() @@ -59,6 +66,8 @@ class Instance { sync(*this), materials(*this), pipelines(*this), + velocity(*this), + camera(*this), main_view(*this), world(*this){}; ~Instance(){}; @@ -83,12 +92,37 @@ class Instance { void draw_viewport(DefaultFramebufferList *dfbl); + bool is_viewport(void) + { + return !DRW_state_is_scene_render(); + } + + bool use_scene_lights(void) const + { + return (!v3d) || + ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + } + + /* Light the scene using the selected HDRI in the viewport shading pop-over. */ + bool use_studio_light(void) const + { + return (v3d) && (((v3d->shading.type == OB_MATERIAL) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD) == 0)) || + ((v3d->shading.type == OB_RENDER) && + ((v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER) == 0))); + } + private: void render_sample(); void mesh_sync(Object *ob, ObjectHandle &ob_handle); void update_eval_members(); + + void set_time(float time); }; } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc index 7452e5c26a4..1676c89d679 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.cc +++ b/source/blender/draw/engines/eevee_next/eevee_material.cc @@ -51,7 +51,6 @@ DefaultSurfaceNodeTree::~DefaultSurfaceNodeTree() MEM_SAFE_FREE(ntree_); } -/* Configure a default nodetree with the given material. */ bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma) { /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */ @@ -75,11 +74,11 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst) { bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); - diffuse_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse"); - diffuse_mat_->nodetree = ntree; - diffuse_mat_->use_nodes = true; + diffuse_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse"); + diffuse_mat->nodetree = ntree; + diffuse_mat->use_nodes = true; /* To use the forward pipeline. */ - diffuse_mat_->blend_method = MA_BM_BLEND; + diffuse_mat->blend_method = MA_BM_BLEND; bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_DIFFUSE); bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); @@ -98,11 +97,11 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst) { bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname); - glossy_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal"); - glossy_mat_->nodetree = ntree; - glossy_mat_->use_nodes = true; + glossy_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal"); + glossy_mat->nodetree = ntree; + glossy_mat->use_nodes = true; /* To use the forward pipeline. */ - glossy_mat_->blend_method = MA_BM_BLEND; + glossy_mat->blend_method = MA_BM_BLEND; bNode *bsdf = nodeAddStaticNode(nullptr, ntree, SH_NODE_BSDF_GLOSSY); bNodeSocket *base_color = nodeFindSocket(bsdf, SOCK_IN, "Color"); @@ -149,14 +148,14 @@ MaterialModule::~MaterialModule() for (Material *mat : material_map_.values()) { delete mat; } - BKE_id_free(nullptr, glossy_mat_); - BKE_id_free(nullptr, diffuse_mat_); + BKE_id_free(nullptr, glossy_mat); + BKE_id_free(nullptr, diffuse_mat); BKE_id_free(nullptr, error_mat_); } void MaterialModule::begin_sync() { - queued_shaders_count_ = 0; + queued_shaders_count = 0; for (Material *mat : material_map_.values()) { mat->init = false; @@ -180,7 +179,7 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, case GPU_MAT_SUCCESS: break; case GPU_MAT_QUEUED: - queued_shaders_count_++; + queued_shaders_count++; blender_mat = (geometry_type == MAT_GEOM_VOLUME) ? BKE_material_default_volume() : BKE_material_default_surface(); matpass.gpumat = inst_.shaders.material_shader_get( @@ -223,7 +222,7 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, /* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the * first "empty" shgroup. This avoids messing the order of subgroups when there is more * nested subgroup (i.e: hair drawing). */ - /* TODO(fclem) Remove material resource binding from the first group creation. */ + /* TODO(@fclem): Remove material resource binding from the first group creation. */ matpass.shgrp = DRW_shgroup_create_sub(grp); DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat); } @@ -232,21 +231,25 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat, return matpass; } -Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeometry geometry_type) +Material &MaterialModule::material_sync(::Material *blender_mat, + eMaterialGeometry geometry_type, + bool has_motion) { eMaterialPipeline surface_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? MAT_PIPE_FORWARD : MAT_PIPE_DEFERRED; eMaterialPipeline prepass_pipe = (blender_mat->blend_method == MA_BM_BLEND) ? - MAT_PIPE_FORWARD_PREPASS : - MAT_PIPE_DEFERRED_PREPASS; + (has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : + MAT_PIPE_FORWARD_PREPASS) : + (has_motion ? MAT_PIPE_DEFERRED_PREPASS_VELOCITY : + MAT_PIPE_DEFERRED_PREPASS); - /* Test */ + /* TEST until we have deferred pipeline up and running. */ surface_pipe = MAT_PIPE_FORWARD; - prepass_pipe = MAT_PIPE_FORWARD_PREPASS; + prepass_pipe = has_motion ? MAT_PIPE_FORWARD_PREPASS_VELOCITY : MAT_PIPE_FORWARD_PREPASS; MaterialKey material_key(blender_mat, geometry_type, surface_pipe); - /* TODO allocate in blocks to avoid memory fragmentation. */ + /* TODO: allocate in blocks to avoid memory fragmentation. */ auto add_cb = [&]() { return new Material(); }; Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb); @@ -270,7 +273,6 @@ Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeomet return mat; } -/* Return correct material or empty default material if slot is empty. */ ::Material *MaterialModule::material_from_slot(Object *ob, int slot) { if (ob->base_flag & BASE_HOLDOUT) { @@ -281,16 +283,12 @@ Material &MaterialModule::material_sync(::Material *blender_mat, eMaterialGeomet if (ob->type == OB_VOLUME) { return BKE_material_default_volume(); } - else { - return BKE_material_default_surface(); - } + return BKE_material_default_surface(); } return ma; } -/* Returned Material references are valid until the next call to this function or - * material_get(). */ -MaterialArray &MaterialModule::material_array_get(Object *ob) +MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion) { material_array_.materials.clear(); material_array_.gpu_materials.clear(); @@ -299,22 +297,23 @@ MaterialArray &MaterialModule::material_array_get(Object *ob) for (auto i : IndexRange(materials_len)) { ::Material *blender_mat = material_from_slot(ob, i); - Material &mat = material_sync(blender_mat, to_material_geometry(ob)); + Material &mat = material_sync(blender_mat, to_material_geometry(ob), has_motion); material_array_.materials.append(&mat); material_array_.gpu_materials.append(mat.shading.gpumat); } return material_array_; } -/* Returned Material references are valid until the next call to this function or - * material_array_get(). */ -Material &MaterialModule::material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type) +Material &MaterialModule::material_get(Object *ob, + bool has_motion, + int mat_nr, + eMaterialGeometry geometry_type) { ::Material *blender_mat = material_from_slot(ob, mat_nr); - Material &mat = material_sync(blender_mat, geometry_type); + Material &mat = material_sync(blender_mat, geometry_type, has_motion); return mat; } /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh index 56f9b077f7a..23165a741b9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_material.hh +++ b/source/blender/draw/engines/eevee_next/eevee_material.hh @@ -27,19 +27,21 @@ class Instance; enum eMaterialPipeline { MAT_PIPE_DEFERRED = 0, - MAT_PIPE_FORWARD = 1, - MAT_PIPE_DEFERRED_PREPASS = 2, - MAT_PIPE_FORWARD_PREPASS = 3, - MAT_PIPE_VOLUME = 4, - MAT_PIPE_SHADOW = 5, + MAT_PIPE_FORWARD, + MAT_PIPE_DEFERRED_PREPASS, + MAT_PIPE_DEFERRED_PREPASS_VELOCITY, + MAT_PIPE_FORWARD_PREPASS, + MAT_PIPE_FORWARD_PREPASS_VELOCITY, + MAT_PIPE_VOLUME, + MAT_PIPE_SHADOW, }; enum eMaterialGeometry { MAT_GEOM_MESH = 0, - MAT_GEOM_CURVES = 1, - MAT_GEOM_GPENCIL = 2, - MAT_GEOM_VOLUME = 3, - MAT_GEOM_WORLD = 4, + MAT_GEOM_CURVES, + MAT_GEOM_GPENCIL, + MAT_GEOM_VOLUME, + MAT_GEOM_WORLD, }; static inline void material_type_from_shader_uuid(uint64_t shader_uuid, @@ -104,7 +106,7 @@ static inline eMaterialGeometry to_material_geometry(const Object *ob) } } -/** Unique key to identify each material in the hashmap. */ +/** Unique key to identify each material in the hash-map. */ struct MaterialKey { Material *mat; uint64_t options; @@ -169,7 +171,7 @@ struct ShaderKey { /** \} */ /* -------------------------------------------------------------------- */ -/** \name Default Material Nodetree +/** \name Default Material Node-Tree * * In order to support materials without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the Material even if @@ -189,6 +191,7 @@ class DefaultSurfaceNodeTree { DefaultSurfaceNodeTree(); ~DefaultSurfaceNodeTree(); + /** Configure a default node-tree with the given material. */ bNodeTree *nodetree_get(::Material *ma); }; @@ -217,8 +220,10 @@ struct MaterialArray { class MaterialModule { public: - ::Material *diffuse_mat_; - ::Material *glossy_mat_; + ::Material *diffuse_mat; + ::Material *glossy_mat; + + int64_t queued_shaders_count = 0; private: Instance &inst_; @@ -232,20 +237,28 @@ class MaterialModule { ::Material *error_mat_; - int64_t queued_shaders_count_ = 0; - public: MaterialModule(Instance &inst); ~MaterialModule(); void begin_sync(); - MaterialArray &material_array_get(Object *ob); - Material &material_get(Object *ob, int mat_nr, eMaterialGeometry geometry_type); + /** + * Returned Material references are valid until the next call to this function or material_get(). + */ + MaterialArray &material_array_get(Object *ob, bool has_motion); + /** + * Returned Material references are valid until the next call to this function or + * material_array_get(). + */ + Material &material_get(Object *ob, bool has_motion, int mat_nr, eMaterialGeometry geometry_type); private: - Material &material_sync(::Material *blender_mat, eMaterialGeometry geometry_type); + Material &material_sync(::Material *blender_mat, + eMaterialGeometry geometry_type, + bool has_motion); + /** Return correct material or empty default material if slot is empty. */ ::Material *material_from_slot(Object *ob, int slot); MaterialPass material_pass_get(::Material *blender_mat, eMaterialPipeline pipeline_type, @@ -254,4 +267,4 @@ class MaterialModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index e31372e770d..33853eba06c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -54,11 +54,17 @@ void ForwardPipeline::sync() { DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state); + prepass_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity", + state | DRW_STATE_WRITE_COLOR); state |= DRW_STATE_CULL_BACK; prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state); + prepass_culled_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity", + state | DRW_STATE_WRITE_COLOR); - DRW_pass_link(prepass_ps_, prepass_culled_ps_); + DRW_pass_link(prepass_ps_, prepass_velocity_ps_); + DRW_pass_link(prepass_velocity_ps_, prepass_culled_ps_); + DRW_pass_link(prepass_culled_ps_, prepass_culled_velocity_ps_); } { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; @@ -110,11 +116,17 @@ DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, G return grp; } -DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat) +DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion) { - DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? prepass_culled_ps_ : - prepass_ps_; + DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? + (has_motion ? prepass_culled_velocity_ps_ : prepass_culled_ps_) : + (has_motion ? prepass_velocity_ps_ : prepass_ps_); DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); + if (has_motion) { + inst_.velocity.bind_resources(grp); + } return grp; } @@ -181,15 +193,19 @@ DRWShadingGroup *ForwardPipeline::prepass_transparent_add(::Material *blender_ma } void ForwardPipeline::render(const DRWView *view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, GPUTexture *depth_tx, GPUTexture *UNUSED(combined_tx)) { - UNUSED_VARS(view, depth_tx); + UNUSED_VARS(view, depth_tx, prepass_fb, combined_fb); // HiZBuffer &hiz = inst_.hiz_front; DRW_stats_group_start("ForwardOpaque"); + GPU_framebuffer_bind(prepass_fb); DRW_draw_pass(prepass_ps_); + // hiz.set_dirty(); // if (inst_.raytracing.enabled()) { @@ -199,6 +215,7 @@ void ForwardPipeline::render(const DRWView *view, // inst_.shadows.set_view(view, depth_tx); + GPU_framebuffer_bind(combined_fb); DRW_draw_pass(opaque_ps_); DRW_stats_group_end(); @@ -218,4 +235,4 @@ void ForwardPipeline::render(const DRWView *view, /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index a5a6847f62e..3bdc718767b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -53,7 +53,9 @@ class ForwardPipeline { Instance &inst_; DRWPass *prepass_ps_ = nullptr; + DRWPass *prepass_velocity_ps_ = nullptr; DRWPass *prepass_culled_ps_ = nullptr; + DRWPass *prepass_culled_velocity_ps_ = nullptr; DRWPass *opaque_ps_ = nullptr; DRWPass *opaque_culled_ps_ = nullptr; DRWPass *transparent_ps_ = nullptr; @@ -72,19 +74,25 @@ class ForwardPipeline { material_opaque_add(blender_mat, gpumat); } - DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat) + DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion) { return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ? prepass_transparent_add(blender_mat, gpumat) : - prepass_opaque_add(blender_mat, gpumat); + prepass_opaque_add(blender_mat, gpumat, has_motion); } DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); - DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat); + DRWShadingGroup *prepass_opaque_add(::Material *blender_mat, + GPUMaterial *gpumat, + bool has_motion); DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat); - void render(const DRWView *view, GPUTexture *depth_tx, GPUTexture *combined_tx); + void render(const DRWView *view, + Framebuffer &prepass_fb, + Framebuffer &combined_fb, + GPUTexture *depth_tx, + GPUTexture *combined_tx); }; /** \} */ @@ -191,10 +199,15 @@ class PipelineModule { { switch (pipeline_type) { case MAT_PIPE_DEFERRED_PREPASS: - // return deferred.prepass_add(blender_mat, gpumat); + // return deferred.prepass_add(blender_mat, gpumat, false); + break; + case MAT_PIPE_DEFERRED_PREPASS_VELOCITY: + // return deferred.prepass_add(blender_mat, gpumat, true); break; case MAT_PIPE_FORWARD_PREPASS: - return forward.prepass_add(blender_mat, gpumat); + return forward.prepass_add(blender_mat, gpumat, false); + case MAT_PIPE_FORWARD_PREPASS_VELOCITY: + return forward.prepass_add(blender_mat, gpumat, true); case MAT_PIPE_DEFERRED: // return deferred.material_add(blender_mat, gpumat); break; @@ -213,4 +226,4 @@ class PipelineModule { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 086c5f9f358..09aa97e49e9 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -25,7 +25,7 @@ ShaderModule *ShaderModule::g_shader_module = nullptr; ShaderModule *ShaderModule::module_get() { if (g_shader_module == nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ g_shader_module = new ShaderModule(); } return g_shader_module; @@ -34,7 +34,7 @@ ShaderModule *ShaderModule::module_get() void ShaderModule::module_free() { if (g_shader_module != nullptr) { - /* TODO(fclem) threadsafety. */ + /* TODO(@fclem) thread-safety. */ delete g_shader_module; g_shader_module = nullptr; } @@ -78,6 +78,8 @@ ShaderModule::~ShaderModule() const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type) { switch (shader_type) { + case VELOCITY_RESOLVE: + return "eevee_velocity_resolve"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; @@ -148,7 +150,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu /** Noop. */ break; case MAT_GEOM_CURVES: - /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */ for (auto &input : info.vertex_inputs_) { if (input.name == "orco") { /** NOTE: Orco is generated from strand position for now. */ @@ -159,18 +161,19 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu } } info.vertex_inputs_.clear(); + info.additional_info("draw_curves_infos"); break; case MAT_GEOM_WORLD: /** * Only orco layer is supported by world and it is procedurally generated. These are here to - * make the attribs_load function calls valids. + * make the attribs_load function calls valid. */ ATTR_FALLTHROUGH; case MAT_GEOM_GPENCIL: /** * Only one uv and one color attribute layer are supported by gpencil objects and they are * already declared in another createInfo. These are here to make the attribs_load - * function calls valids. + * function calls valid. */ for (auto &input : info.vertex_inputs_) { global_vars << input.type << " " << input.name << ";\n"; @@ -190,7 +193,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first(); /* Globals the attrib_load() can write to when it is in the fragment shader. */ global_vars << "struct " << iface.name << " {\n"; - for (auto &inout : iface.inouts) { + for (const auto &inout : iface.inouts) { global_vars << " " << inout.type << " " << inout.name << ";\n"; } global_vars << "};\n"; @@ -289,6 +292,10 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu break; default: switch (pipeline_type) { + case MAT_PIPE_FORWARD_PREPASS_VELOCITY: + case MAT_PIPE_DEFERRED_PREPASS_VELOCITY: + info.additional_info("eevee_surf_depth", "eevee_velocity_geom"); + break; case MAT_PIPE_FORWARD_PREPASS: case MAT_PIPE_DEFERRED_PREPASS: case MAT_PIPE_SHADOW: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index ba7c97b3b6a..0f42e880a10 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -26,7 +26,9 @@ namespace blender::eevee { /* Keep alphabetical order and clean prefix. */ enum eShaderType { - MAX_SHADER_TYPE = 0, + VELOCITY_RESOLVE = 0, + + MAX_SHADER_TYPE, }; /** @@ -36,7 +38,7 @@ class ShaderModule { private: std::array<GPUShader *, MAX_SHADER_TYPE> shaders_; - /** Shared shader module accross all engine instances. */ + /** Shared shader module across all engine instances. */ static ShaderModule *g_shader_module; public: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 2225ccac43a..1261c855a82 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -19,6 +19,7 @@ namespace blender::eevee { using draw::Framebuffer; +using draw::SwapChain; using draw::Texture; using draw::TextureFromPool; @@ -27,7 +28,101 @@ using draw::TextureFromPool; #define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 /* -------------------------------------------------------------------- */ -/** \name Raytracing +/** \name Camera + * \{ */ + +enum eCameraType : uint32_t { + CAMERA_PERSP = 0u, + CAMERA_ORTHO = 1u, + CAMERA_PANO_EQUIRECT = 2u, + CAMERA_PANO_EQUISOLID = 3u, + CAMERA_PANO_EQUIDISTANT = 4u, + CAMERA_PANO_MIRROR = 5u +}; + +static inline bool is_panoramic(eCameraType type) +{ + return type > CAMERA_ORTHO; +} + +struct CameraData { + /* View Matrices of the camera, not from any view! */ + float4x4 persmat; + float4x4 persinv; + float4x4 viewmat; + float4x4 viewinv; + float4x4 winmat; + float4x4 wininv; + /** Camera UV scale and bias. Also known as `viewcamtexcofac`. */ + float2 uv_scale; + float2 uv_bias; + /** Panorama parameters. */ + float2 equirect_scale; + float2 equirect_scale_inv; + float2 equirect_bias; + float fisheye_fov; + float fisheye_lens; + /** Clipping distances. */ + float clip_near; + float clip_far; + /** Film pixel filter radius. */ + float filter_size; + eCameraType type; +}; +BLI_STATIC_ASSERT_ALIGN(CameraData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * \{ */ + +#define VELOCITY_INVALID 512.0 + +enum eVelocityStep : uint32_t { + STEP_PREVIOUS = 0, + STEP_NEXT = 1, + STEP_CURRENT = 2, +}; + +struct VelocityObjectIndex { + /** Offset inside #VelocityObjectBuf for each timestep. Indexed using eVelocityStep. */ + int3 ofs; + /** Temporary index to copy this to the #VelocityIndexBuf. */ + uint resource_id; + +#ifdef __cplusplus + VelocityObjectIndex() : ofs(-1, -1, -1), resource_id(-1){}; +#endif +}; +BLI_STATIC_ASSERT_ALIGN(VelocityObjectIndex, 16) + +struct VelocityGeometryIndex { + /** Offset inside #VelocityGeometryBuf for each timestep. Indexed using eVelocityStep. */ + int3 ofs; + /** If true, compute deformation motion blur. */ + bool1 do_deform; + /** Length of data inside #VelocityGeometryBuf for each timestep. Indexed using eVelocityStep. */ + int3 len; + + int _pad0; + +#ifdef __cplusplus + VelocityGeometryIndex() : ofs(-1, -1, -1), do_deform(false), len(-1, -1, -1), _pad0(1){}; +#endif +}; +BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) + +struct VelocityIndex { + VelocityObjectIndex obj; + VelocityGeometryIndex geo; +}; +BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ray-Tracing * \{ */ enum eClosureBits : uint32_t { @@ -83,5 +178,10 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer) #ifdef __cplusplus +using CameraDataBuf = draw::UniformBuffer<CameraData>; +using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>; +using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>; +using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>; + } // namespace blender::eevee #endif diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc index efa5fdc89ab..42af251d770 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc @@ -104,7 +104,9 @@ static inline void shgroup_geometry_call(DRWShadingGroup *grp, void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) { - MaterialArray &material_array = inst_.materials.material_array_get(ob); + bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + + MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion); GPUBatch **mat_geom = DRW_cache_object_surface_material_get( ob, material_array.gpu_materials.data(), material_array.gpu_materials.size()); @@ -129,9 +131,6 @@ void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle) is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent; } - UNUSED_VARS(ob_handle); - // shading_passes.velocity.mesh_add(ob, ob_handle); - // shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend); } @@ -156,8 +155,11 @@ struct gpIterData { int vcount = 0; bool instancing = false; - gpIterData(Instance &inst_, Object *ob_) - : inst(inst_), ob(ob_), material_array(inst_.materials.material_array_get(ob_)) + gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle) + : inst(inst_), + ob(ob_), + material_array(inst_.materials.material_array_get( + ob_, inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc))) { cfra = DEG_get_ctime(inst.depsgraph); }; @@ -253,16 +255,12 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle) /* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */ return; - gpIterData iter(inst_, ob); + gpIterData iter(inst_, ob, ob_handle); BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter); gpencil_drawcall_flush(iter); - UNUSED_VARS(ob_handle); - /* TODO(fclem) Gpencil velocity. */ - // shading_passes.velocity.gpencil_add(ob, ob_handle); - // bool is_caster = true; /* TODO material.shadow.shgrp. */ // bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */ // shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend); @@ -304,12 +302,13 @@ void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData * mat_nr = part_settings->omat; } - Material &material = inst_.materials.material_get(ob, mat_nr - 1, MAT_GEOM_CURVES); + bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc); + Material &material = inst_.materials.material_get(ob, has_motion, mat_nr - 1, MAT_GEOM_CURVES); shgroup_curves_call(material.shading, ob, part_sys, modifier_data); shgroup_curves_call(material.prepass, ob, part_sys, modifier_data); shgroup_curves_call(material.shadow, ob, part_sys, modifier_data); - UNUSED_VARS(ob_handle); + /* TODO(fclem) Hair velocity. */ // shading_passes.velocity.gpencil_add(ob, ob_handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh index 51e0f86fe5c..bd8147a2882 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sync.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh @@ -27,7 +27,8 @@ class Instance; /* -------------------------------------------------------------------- */ /** \name ObjectKey * - * Unique key to identify each object in the hashmap. + * Unique key to identify each object in the hash-map. + * Note that we get a unique key for each object component. * \{ */ struct ObjectKey { diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc new file mode 100644 index 00000000000..9f8dce43910 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -0,0 +1,420 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects between frames updates. + * + * #VelocityModule contains all motion steps data and logic. + * #VelocityPass contains the resolve pass for static geometry. + * #VelocityView is a per view instance that contain the velocity buffer. + */ + +#include "BKE_duplilist.h" +#include "BKE_object.h" +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" +#include "DNA_rigidbody_types.h" + +#include "eevee_instance.hh" +// #include "eevee_renderpasses.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +void VelocityModule::init() +{ +#if 0 /* TODO renderpasses */ + if (inst_.render && (inst_.render_passes.vector != nullptr)) { + /* No motion blur and the vector pass was requested. Do the step sync here. */ + const Scene *scene = inst_.scene; + float initial_time = scene->r.cfra + scene->r.subframe; + step_sync(STEP_PREVIOUS, initial_time - 1.0f); + step_sync(STEP_NEXT, initial_time + 1.0f); + inst_.set_time(initial_time); + } +#endif +} + +static void step_object_sync_render(void *velocity, + Object *ob, + RenderEngine *UNUSED(engine), + Depsgraph *UNUSED(depsgraph)) +{ + ObjectKey object_key(ob); + reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key); +} + +void VelocityModule::step_sync(eVelocityStep step, float time) +{ + inst_.set_time(time); + step_ = step; + object_steps_usage[step_] = 0; + step_camera_sync(); + DRW_render_object_iter(this, inst_.render, inst_.depsgraph, step_object_sync_render); +} + +void VelocityModule::step_camera_sync() +{ + inst_.camera.sync(); + *camera_steps[step_] = inst_.camera.data_get(); +} + +bool VelocityModule::step_object_sync(Object *ob, + ObjectKey &object_key, + int /*IDRecalcFlag*/ recalc) +{ + bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM); + /* NOTE: Fragile. This will only work with 1 frame of lag since we can't record every geometry + * just in case there might be an update the next frame. */ + bool has_deform = object_is_deform(ob) || (recalc & ID_RECALC_GEOMETRY); + + if (!has_motion && !has_deform) { + return false; + } + + uint32_t resource_id = DRW_object_resource_id_get(ob); + + /* Object motion. */ + /* FIXME(fclem) As we are using original objects pointers, there is a chance the previous + * object key matches a totally different object if the scene was changed by user or python + * callback. In this case, we cannot correctly match objects between updates. + * What this means is that there will be incorrect motion vectors for these objects. + * We live with that until we have a correct way of identifying new objects. */ + VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key); + vel.obj.ofs[step_] = object_steps_usage[step_]++; + vel.obj.resource_id = resource_id; + vel.id = (ID *)ob->data; + object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = ob->obmat; + if (step_ == STEP_CURRENT) { + /* Replace invalid steps. Can happen if object was hidden in one of those steps. */ + if (vel.obj.ofs[STEP_PREVIOUS] == -1) { + vel.obj.ofs[STEP_PREVIOUS] = object_steps_usage[STEP_PREVIOUS]++; + object_steps[STEP_PREVIOUS]->get_or_resize(vel.obj.ofs[STEP_PREVIOUS]) = ob->obmat; + } + if (vel.obj.ofs[STEP_NEXT] == -1) { + vel.obj.ofs[STEP_NEXT] = object_steps_usage[STEP_NEXT]++; + object_steps[STEP_NEXT]->get_or_resize(vel.obj.ofs[STEP_NEXT]) = ob->obmat; + } + } + + /* Geometry motion. */ + if (has_deform) { + auto add_cb = [&]() { + VelocityGeometryData data; + switch (ob->type) { + case OB_CURVES: + data.pos_buf = DRW_curves_pos_buffer_get(ob); + break; + default: + data.pos_buf = DRW_cache_object_pos_vertbuf_get(ob); + break; + } + return data; + }; + + const VelocityGeometryData &data = geometry_map.lookup_or_add_cb(vel.id, add_cb); + + if (data.pos_buf == nullptr) { + has_deform = false; + } + } + + /* Avoid drawing object that has no motions but were tagged as such. */ + if (step_ == STEP_CURRENT && has_motion == true && has_deform == false) { + float4x4 &obmat_curr = (*object_steps[STEP_CURRENT])[vel.obj.ofs[STEP_CURRENT]]; + float4x4 &obmat_prev = (*object_steps[STEP_PREVIOUS])[vel.obj.ofs[STEP_PREVIOUS]]; + float4x4 &obmat_next = (*object_steps[STEP_NEXT])[vel.obj.ofs[STEP_NEXT]]; + if (inst_.is_viewport()) { + has_motion = (obmat_curr != obmat_prev); + } + else { + has_motion = (obmat_curr != obmat_prev || obmat_curr != obmat_next); + } + } + +#if 0 + if (!has_motion && !has_deform) { + std::cout << "Detected no motion on " << ob->id.name << std::endl; + } + if (has_deform) { + std::cout << "Geometry Motion on " << ob->id.name << std::endl; + } + if (has_motion) { + std::cout << "Object Motion on " << ob->id.name << std::endl; + } +#endif + + if (!has_motion && !has_deform) { + return false; + } + + /* TODO(@fclem): Reset sampling here? Should ultimately be covered by depsgraph update tags. */ + // inst_.sampling.reset(); + + return true; +} + +/** + * Moves next frame data to previous frame data. Nullify next frame data. + * IMPORTANT: This runs AFTER drawing in the viewport (so after `begin_sync()`) but BEFORE drawing + * in render mode (so before `begin_sync()`). In viewport the data will be used the next frame. + */ +void VelocityModule::step_swap() +{ + { + /* Now that vertex buffers are guaranteed to be updated, proceed with + * offset computation and copy into the geometry step buffer. */ + uint dst_ofs = 0; + for (VelocityGeometryData &geom : geometry_map.values()) { + uint src_len = GPU_vertbuf_get_vertex_len(geom.pos_buf); + geom.len = src_len; + geom.ofs = dst_ofs; + dst_ofs += src_len; + } + /* TODO(@fclem): Fail gracefully (disable motion blur + warning print) if + `tot_len * sizeof(float4)` is greater than max SSBO size. */ + geometry_steps[step_]->resize(max_ii(16, dst_ofs)); + + for (VelocityGeometryData &geom : geometry_map.values()) { + GPU_storagebuf_copy_sub_from_vertbuf(*geometry_steps[step_], + geom.pos_buf, + geom.ofs * sizeof(float4), + 0, + geom.len * sizeof(float4)); + } + /* Copy back the #VelocityGeometryIndex into #VelocityObjectData which are + * indexed using persistent keys (unlike geometries which are indexed by volatile ID). */ + for (VelocityObjectData &vel : velocity_map.values()) { + const VelocityGeometryData &geom = geometry_map.lookup_default(vel.id, + VelocityGeometryData()); + vel.geo.len[step_] = geom.len; + vel.geo.ofs[step_] = geom.ofs; + /* Avoid reuse. */ + vel.id = nullptr; + } + + geometry_map.clear(); + } + + auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) { + SWAP(VelocityObjectBuf *, object_steps[step_a], object_steps[step_b]); + SWAP(VelocityGeometryBuf *, geometry_steps[step_a], geometry_steps[step_b]); + SWAP(CameraDataBuf *, camera_steps[step_a], camera_steps[step_b]); + + for (VelocityObjectData &vel : velocity_map.values()) { + vel.obj.ofs[step_a] = vel.obj.ofs[step_b]; + vel.obj.ofs[step_b] = (uint)-1; + vel.geo.ofs[step_a] = vel.geo.ofs[step_b]; + vel.geo.len[step_a] = vel.geo.len[step_b]; + vel.geo.ofs[step_b] = (uint)-1; + vel.geo.len[step_b] = (uint)-1; + } + }; + + if (inst_.is_viewport()) { + /* For viewport we only use the last rendered redraw as previous frame. + * We swap current with previous step at the end of a redraw. + * We do not support motion blur as it is rendered to avoid conflicting motions + * for temporal reprojection. */ + swap_steps(eVelocityStep::STEP_PREVIOUS, eVelocityStep::STEP_CURRENT); + } + else { + /* Render case: The STEP_CURRENT is left untouched. */ + swap_steps(eVelocityStep::STEP_PREVIOUS, eVelocityStep::STEP_NEXT); + } +} + +void VelocityModule::begin_sync() +{ + if (inst_.is_viewport()) { + /* Viewport always evaluate current step. */ + step_ = STEP_CURRENT; + } + step_camera_sync(); + object_steps_usage[step_] = 0; +} + +/* This is the end of the current frame sync. Not the step_sync. */ +void VelocityModule::end_sync() +{ + Vector<ObjectKey, 0> deleted_obj; + + uint32_t max_resource_id_ = 0u; + + for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) { + if (item.value.obj.resource_id == (uint)-1) { + deleted_obj.append(item.key); + } + else { + max_resource_id_ = max_uu(max_resource_id_, item.value.obj.resource_id); + } + } + + if (deleted_obj.size() > 0) { + // inst_.sampling.reset(); + } + + for (auto key : deleted_obj) { + velocity_map.remove(key); + } + + indirection_buf.resize(power_of_2_max_u(max_resource_id_ + 1)); + + /* Avoid uploading more data to the GPU as well as an extra level of + * indirection on the GPU by copying back offsets the to VelocityIndex. */ + for (VelocityObjectData &vel : velocity_map.values()) { + /* Disable deform if vertex count mismatch. */ + if (inst_.is_viewport()) { + /* Current geometry step will be copied at the end of the frame. + * Thus vel.geo.len[STEP_CURRENT] is not yet valid and the current length is manually + * retrieved. */ + GPUVertBuf *pos_buf = geometry_map.lookup_default(vel.id, VelocityGeometryData()).pos_buf; + vel.geo.do_deform = pos_buf != nullptr && + (vel.geo.len[STEP_PREVIOUS] == GPU_vertbuf_get_vertex_len(pos_buf)); + } + else { + vel.geo.do_deform = (vel.geo.len[STEP_PREVIOUS] == vel.geo.len[STEP_CURRENT]) && + (vel.geo.len[STEP_NEXT] == vel.geo.len[STEP_CURRENT]); + } + indirection_buf[vel.obj.resource_id] = vel; + /* Reset for next sync. */ + vel.obj.resource_id = (uint)-1; + } + + object_steps[STEP_PREVIOUS]->push_update(); + object_steps[STEP_NEXT]->push_update(); + camera_steps[STEP_PREVIOUS]->push_update(); + camera_steps[STEP_CURRENT]->push_update(); + camera_steps[STEP_NEXT]->push_update(); + indirection_buf.push_update(); + + { + resolve_ps_ = DRW_pass_create("Velocity.Resolve", (DRWState)0); + GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_RESOLVE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_); + DRW_shgroup_uniform_image_ref(grp, "velocity_view_img", &velocity_view_tx_); + DRW_shgroup_uniform_image_ref(grp, "velocity_camera_img", &velocity_camera_tx_); + DRW_shgroup_uniform_block(grp, "camera_prev", *camera_steps[STEP_PREVIOUS]); + DRW_shgroup_uniform_block(grp, "camera_curr", *camera_steps[STEP_CURRENT]); + DRW_shgroup_uniform_block(grp, "camera_next", *camera_steps[STEP_NEXT]); + DRW_shgroup_call_compute_ref(grp, resolve_dispatch_size_); + } +} + +bool VelocityModule::object_has_velocity(const Object *ob) +{ +#if 0 + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + /* For now we assume dupli objects are moving. */ + const bool is_dupli = (ob->base_flag & BASE_FROM_DUPLI) != 0; + const bool object_moves = is_dupli || has_rigidbody || BKE_object_moves_in_time(ob, true); +#else + UNUSED_VARS(ob); + /* BKE_object_moves_in_time does not work in some cases. + * Better detect non moving object after evaluation. */ + const bool object_moves = true; +#endif + return object_moves; +} + +bool VelocityModule::object_is_deform(const Object *ob) +{ + RigidBodyOb *rbo = ob->rigidbody_object; + /* Active rigidbody objects only, as only those are affected by sim. */ + const bool has_rigidbody = (rbo && (rbo->type == RBO_TYPE_ACTIVE)); + const bool is_deform = BKE_object_is_deform_modified(inst_.scene, (Object *)ob) || + (has_rigidbody && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0); + + return is_deform; +} + +void VelocityModule::bind_resources(DRWShadingGroup *grp) +{ + /* For viewport, only previous motion is supported. + * Still bind previous step to avoid undefined behavior. */ + eVelocityStep next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT; + DRW_shgroup_storage_block_ref(grp, "velocity_obj_prev_buf", &(*object_steps[STEP_PREVIOUS])); + DRW_shgroup_storage_block_ref(grp, "velocity_obj_next_buf", &(*object_steps[next])); + DRW_shgroup_storage_block_ref(grp, "velocity_geo_prev_buf", &(*geometry_steps[STEP_PREVIOUS])); + DRW_shgroup_storage_block_ref(grp, "velocity_geo_next_buf", &(*geometry_steps[next])); + DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*camera_steps[STEP_PREVIOUS])); + DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*camera_steps[STEP_CURRENT])); + DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*camera_steps[next])); + DRW_shgroup_storage_block_ref(grp, "velocity_indirection_buf", &indirection_buf); +} + +/* Resolve pass for static geometry and to camera space projection. */ +void VelocityModule::resolve_camera_motion(GPUTexture *depth_tx, + GPUTexture *velocity_view_tx, + GPUTexture *velocity_camera_tx) +{ + input_depth_tx_ = depth_tx; + velocity_view_tx_ = velocity_view_tx; + velocity_camera_tx_ = velocity_camera_tx; + + resolve_dispatch_size_.x = divide_ceil_u(GPU_texture_width(depth_tx), 8); + resolve_dispatch_size_.y = divide_ceil_u(GPU_texture_height(depth_tx), 8); + + DRW_draw_pass(resolve_ps_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity View + * \{ */ + +void VelocityView::sync() +{ + /* TODO: Remove. */ + velocity_view_tx_.sync(); + velocity_camera_tx_.sync(); +} + +void VelocityView::acquire(int2 extent) +{ + /* WORKAROUND: View name should be unique and static. + * With this, we can reuse the same texture across views. */ + DrawEngineType *owner = (DrawEngineType *)view_name_.c_str(); + + /* Only RG16F when only doing only reprojection or motion blur. */ + eGPUTextureFormat format = inst_.is_viewport() ? GPU_RG16F : GPU_RGBA16F; + velocity_view_tx_.acquire(extent, format, owner); + if (false /* TODO(fclem): Panoramic camera. */) { + velocity_camera_tx_.acquire(extent, format, owner); + } + else { + velocity_camera_tx_.acquire(int2(1), format, owner); + } +} + +void VelocityView::resolve(GPUTexture *depth_tx) +{ + inst_.velocity.resolve_camera_motion(depth_tx, velocity_view_tx_, velocity_camera_tx_); +} + +void VelocityView::release() +{ + velocity_view_tx_.release(); + velocity_camera_tx_.release(); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh new file mode 100644 index 00000000000..1bfd9f8c18f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The velocity pass outputs motion vectors to use for either + * temporal re-projection or motion blur. + * + * It is the module that tracks the objects data between frames updates. + */ + +#pragma once + +#include "BLI_map.hh" + +#include "eevee_shader_shared.hh" +#include "eevee_sync.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name VelocityModule + * + * \{ */ + +/** Container for scene velocity data. */ +class VelocityModule { + friend class VelocityView; + + public: + struct VelocityObjectData : public VelocityIndex { + /** ID to retrieve the corresponding #VelocityGeometryData after copy. */ + ID *id; + }; + struct VelocityGeometryData { + /** VertBuf not yet ready to be copied to the #VelocityGeometryBuf. */ + GPUVertBuf *pos_buf = nullptr; + /* Offset in the #VelocityGeometryBuf to the start of the data. In vertex. */ + int ofs; + /* Length of the vertex buffer. In vertex. */ + int len; + }; + /** + * The map contains indirection indices to the obmat and geometry in each step buffer. + * Note that each object component gets its own resource id so one component correspond to one + * geometry offset. + */ + Map<ObjectKey, VelocityObjectData> velocity_map; + /** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID *. Empty after */ + Map<ID *, VelocityGeometryData> geometry_map; + /** Contains all objects matrices for each time step. */ + std::array<VelocityObjectBuf *, 3> object_steps; + /** Contains all Geometry steps from deforming objects for each time step. */ + std::array<VelocityGeometryBuf *, 3> geometry_steps; + /** Number of occupied slot in each `object_steps`. */ + int3 object_steps_usage = int3(0); + /** Buffer of all #VelocityIndex used in this frame. Indexed by draw manager resource id. */ + VelocityIndexBuf indirection_buf; + + /** + * Copies of camera data. One for previous and one for next time step. + */ + std::array<CameraDataBuf *, 3> camera_steps; + + private: + Instance &inst_; + + eVelocityStep step_ = STEP_CURRENT; + + DRWPass *resolve_ps_ = nullptr; + + /** Reference only. Not owned. */ + GPUTexture *input_depth_tx_; + GPUTexture *velocity_view_tx_; + GPUTexture *velocity_camera_tx_; + + int3 resolve_dispatch_size_ = int3(1, 1, 1); + + public: + VelocityModule(Instance &inst) : inst_(inst) + { + for (VelocityObjectBuf *&step_buf : object_steps) { + step_buf = new VelocityObjectBuf(); + } + for (VelocityGeometryBuf *&step_buf : geometry_steps) { + step_buf = new VelocityGeometryBuf(); + } + for (CameraDataBuf *&step_buf : camera_steps) { + step_buf = new CameraDataBuf(); + } + }; + + ~VelocityModule() + { + for (VelocityObjectBuf *step_buf : object_steps) { + delete step_buf; + } + for (VelocityGeometryBuf *step_buf : geometry_steps) { + delete step_buf; + } + for (CameraDataBuf *step_buf : camera_steps) { + delete step_buf; + } + } + + void init(); + + void step_camera_sync(); + void step_sync(eVelocityStep step, float time); + + /* Gather motion data. Returns true if the object **can** have motion. */ + bool step_object_sync(Object *ob, ObjectKey &ob_key, int recalc = 0); + + /* Moves next frame data to previous frame data. Nullify next frame data. */ + void step_swap(); + + void begin_sync(); + void end_sync(); + + void bind_resources(DRWShadingGroup *grp); + + private: + bool object_has_velocity(const Object *ob); + bool object_is_deform(const Object *ob); + + void resolve_camera_motion(GPUTexture *depth_tx, + GPUTexture *velocity_view_tx, + GPUTexture *velocity_camera_tx); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity + * + * \{ */ + +/** + * Per view module. + */ +class VelocityView { + private: + Instance &inst_; + + StringRefNull view_name_; + + TextureFromPool velocity_camera_tx_ = {"velocity_camera_tx_"}; + TextureFromPool velocity_view_tx_ = {"velocity_view_tx_"}; + + public: + VelocityView(Instance &inst, const char *name) : inst_(inst), view_name_(name){}; + ~VelocityView(){}; + + void sync(); + + void acquire(int2 extent); + void release(); + + void resolve(GPUTexture *depth_tx); + + /** + * Getters + **/ + GPUTexture *view_vectors_get() const + { + return velocity_view_tx_; + } + GPUTexture *camera_vectors_get() const + { + return (velocity_camera_tx_.is_valid()) ? velocity_camera_tx_ : velocity_view_tx_; + } +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index df45200c712..e21342c5ef6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -9,7 +9,7 @@ * - The entire main view. * - A fragment of the main view (for panoramic projections). * - A shadow map view. - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -40,7 +40,7 @@ void ShadingView::sync(int2 render_extent_) int64_t render_pixel_count = render_extent_.x * (int64_t)render_extent_.y; /* Divide pixel count between the 6 views. Rendering to a square target. */ extent_[0] = extent_[1] = ceilf(sqrtf(1 + (render_pixel_count / 6))); - /* TODO(fclem) Clip unused views heres. */ + /* TODO(@fclem): Clip unused views here. */ is_enabled_ = true; } else { @@ -60,8 +60,8 @@ void ShadingView::sync(int2 render_extent_) const float(*viewmat_p)[4] = viewmat.ptr(), (*winmat_p)[4] = winmat.ptr(); #if 0 if (false /* inst_.camera.is_panoramic() */) { - /* TODO(fclem) Overscans. */ - /* For now a mandatory 5% overscan for DoF. */ + /* TODO(@fclem) Over-scans. */ + /* For now a mandatory 5% over-scan for DoF. */ float side = data.clip_near * 1.05f; float near = data.clip_near; float far = data.clip_far; @@ -86,7 +86,7 @@ void ShadingView::sync(int2 render_extent_) // dof_.sync(winmat_p, extent_); // mb_.sync(extent_); - // velocity_.sync(extent_); + velocity_.sync(); // rt_buffer_opaque_.sync(extent_); // rt_buffer_refract_.sync(extent_); // inst_.hiz_back.view_sync(extent_); @@ -108,22 +108,30 @@ void ShadingView::render() * With this, we can reuse the same texture across views. */ DrawEngineType *owner = (DrawEngineType *)name_; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, extent_); combined_tx_.acquire(extent_, GPU_RGBA16F, owner); - view_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_)); + velocity_.acquire(extent_); + // combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), GPU_ATTACHMENT_TEXTURE(combined_tx_)); + // prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_), + // GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get())); + combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)); + prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get())); update_view(); DRW_stats_group_start(name_); // DRW_view_set_active(render_view_); + float4 clear_velocity(VELOCITY_INVALID); + GPU_framebuffer_bind(prepass_fb_); + GPU_framebuffer_clear_color(prepass_fb_, clear_velocity); /* Alpha stores transmittance. So start at 1. */ float4 clear_color = {0.0f, 0.0f, 0.0f, 1.0f}; - // GPU_framebuffer_bind(view_fb_); - // GPU_framebuffer_clear_color_depth(view_fb_, clear_color, 1.0f); - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - GPU_framebuffer_bind(dfbl->default_fb); - GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_color, 1.0f); + GPU_framebuffer_bind(combined_fb_); + GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f); inst_.pipelines.world.render(); @@ -134,12 +142,13 @@ void ShadingView::render() // inst_.lookdev.render_overlay(view_fb_); - inst_.pipelines.forward.render(render_view_, depth_tx_, combined_tx_); + inst_.pipelines.forward.render(render_view_, prepass_fb_, combined_fb_, depth_tx_, combined_tx_); // inst_.lights.debug_draw(view_fb_); // inst_.shadows.debug_draw(view_fb_); - // velocity_.render(depth_tx_); + // velocity_.resolve(depth_tx_); + velocity_.resolve(dtxl->depth); // if (inst_.render_passes.vector) { // inst_.render_passes.vector->accumulate(velocity_.camera_vectors_get(), sub_view_); @@ -159,6 +168,7 @@ void ShadingView::render() combined_tx_.release(); postfx_tx_.release(); + velocity_.release(); } GPUTexture *ShadingView::render_post(GPUTexture *input_tx) @@ -205,4 +215,4 @@ void ShadingView::update_view() /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index ab7b5722de1..95ec1760c63 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -8,7 +8,7 @@ * A view is either: * - The entire main view. * - A portion of the main view (for panoramic projections). - * - A lightprobe view (either planar, cubemap, irradiance grid). + * - A light-probe view (either planar, cube-map, irradiance grid). * * A pass is a container for scene data. It is view agnostic but has specific logic depending on * its type. Passes are shared between views. @@ -21,6 +21,7 @@ #include "eevee_camera.hh" #include "eevee_pipeline.hh" #include "eevee_shader.hh" +#include "eevee_velocity.hh" namespace blender::eevee { @@ -43,13 +44,14 @@ class ShadingView { /** Post-fx modules. */ // DepthOfField dof_; // MotionBlur mb_; - // Velocity velocity_; + VelocityView velocity_; /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ // RaytraceBuffer rt_buffer_opaque_; // RaytraceBuffer rt_buffer_refract_; - Framebuffer view_fb_; + Framebuffer prepass_fb_; + Framebuffer combined_fb_; Texture depth_tx_; TextureFromPool combined_tx_; TextureFromPool postfx_tx_; @@ -69,7 +71,7 @@ class ShadingView { public: ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4]) - : inst_(inst), name_(name), face_matrix_(face_matrix){}; + : inst_(inst), name_(name), face_matrix_(face_matrix), velocity_(inst, name){}; ~ShadingView(){}; @@ -154,4 +156,4 @@ class MainView { /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index 939f6087137..b9cb24fe30a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -64,19 +64,17 @@ void World::sync() // } ::World *bl_world = inst_.scene->world; - if (bl_world == nullptr) { // bl_world = BKE_world_default(); return; } - else { - WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); - if (wo_handle.recalc != 0) { - // inst_.lightprobes.set_world_dirty(); - } - wo_handle.reset_recalc_flag(); + WorldHandle &wo_handle = inst_.sync.sync_world(bl_world); + + if (wo_handle.recalc != 0) { + // inst_.lightprobes.set_world_dirty(); } + wo_handle.reset_recalc_flag(); /* TODO(fclem) This should be detected to scene level. */ ::World *orig_world = (::World *)DEG_get_original_id(&bl_world->id); diff --git a/source/blender/draw/engines/eevee_next/eevee_world.hh b/source/blender/draw/engines/eevee_next/eevee_world.hh index 56554051eea..05177928436 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.hh +++ b/source/blender/draw/engines/eevee_next/eevee_world.hh @@ -18,7 +18,7 @@ namespace blender::eevee { class Instance; /* -------------------------------------------------------------------- */ -/** \name Default World Nodetree +/** \name Default World Node-Tree * * In order to support worlds without nodetree we reuse and configure a standalone nodetree that * we pass for shader generation. The GPUMaterial is still stored inside the World even if diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index 3c5acf62e30..1b113e529b6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -112,6 +112,7 @@ float attr_load_float(float attr) /** \name Curve * * Curve objects loads attributes from buffers through sampler buffers. + * Per attribute scope follows loading order. * \{ */ # ifdef OBINFO_LIB @@ -122,6 +123,22 @@ vec3 attr_load_orco(vec4 orco) return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz; } # endif + +int g_curves_attr_id = 0; + +/* Return the index to use for looking up the attribute value in the sampler + * based on the attribute scope (point or spline). */ +int curves_attribute_element_id() +{ + int id = interp.curves_strand_id; + if (drw_curves.is_point_attribute[g_curves_attr_id] != 0) { + id = hair_get_base_id(); + } + + g_curves_attr_id += 1; + return id; +} + vec4 attr_load_tangent(samplerBuffer cd_buf) { /* Not supported for the moment. */ @@ -137,19 +154,19 @@ vec4 attr_load_color(samplerBuffer cd_buf) } vec4 attr_load_vec4(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgba; + return texelFetch(cd_buf, curves_attribute_element_id()).rgba; } vec3 attr_load_vec3(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rgb; + return texelFetch(cd_buf, curves_attribute_element_id()).rgb; } vec2 attr_load_vec2(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).rg; + return texelFetch(cd_buf, curves_attribute_element_id()).rg; } float attr_load_float(samplerBuffer cd_buf) { - return texelFetch(cd_buf, interp.curves_strand_id).r; + return texelFetch(cd_buf, curves_attribute_element_id()).r; } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl new file mode 100644 index 00000000000..f79e9102d76 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl @@ -0,0 +1,166 @@ + +/** + * Camera projection / uv functions and utils. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Panoramic Projections + * + * Adapted from Cycles to match EEVEE's coordinate system. + * \{ */ + +vec2 camera_equirectangular_from_direction(CameraData cam, vec3 dir) +{ + float phi = atan(-dir.z, dir.x); + float theta = acos(dir.y / length(dir)); + return (vec2(phi, theta) - cam.equirect_bias) * cam.equirect_scale_inv; +} + +vec3 camera_equirectangular_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.equirect_scale + cam.equirect_bias; + float phi = uv.x; + float theta = uv.y; + float sin_theta = sin(theta); + return vec3(sin_theta * cos(phi), cos(theta), -sin_theta * sin(phi)); +} + +vec2 camera_fisheye_from_direction(CameraData cam, vec3 dir) +{ + float r = atan(length(dir.xy), -dir.z) / cam.fisheye_fov; + float phi = atan(dir.y, dir.x); + vec2 uv = r * vec2(cos(phi), sin(phi)) + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_fisheye_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + uv = (uv - 0.5) * 2.0; + float r = length(uv); + if (r > 1.0) { + return vec3(0.0); + } + float phi = safe_acos(uv.x * safe_rcp(r)); + float theta = r * cam.fisheye_fov * 0.5; + if (uv.y < 0.0) { + phi = -phi; + } + return vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta)); +} + +vec2 camera_mirror_ball_from_direction(CameraData cam, vec3 dir) +{ + dir = normalize(dir); + dir.z -= 1.0; + dir *= safe_rcp(2.0 * safe_sqrt(-0.5 * dir.z)); + vec2 uv = 0.5 * dir.xy + 0.5; + return (uv - cam.uv_bias) / cam.uv_scale; +} + +vec3 camera_mirror_ball_to_direction(CameraData cam, vec2 uv) +{ + uv = uv * cam.uv_scale + cam.uv_bias; + vec3 dir; + dir.xy = uv * 2.0 - 1.0; + if (len_squared(dir.xy) > 1.0) { + return vec3(0.0); + } + dir.z = -safe_sqrt(1.0 - sqr(dir.x) - sqr(dir.y)); + const vec3 I = vec3(0.0, 0.0, 1.0); + return reflect(I, dir); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Regular projections + * \{ */ + +vec3 camera_view_from_uv(mat4 projmat, vec2 uv) +{ + return project_point(projmat, vec3(uv * 2.0 - 1.0, 0.0)); +} + +vec2 camera_uv_from_view(mat4 projmat, bool is_persp, vec3 vV) +{ + vec4 tmp = projmat * vec4(vV, 1.0); + if (is_persp && tmp.w <= 0.0) { + /* Return invalid coordinates for points behind the camera. + * This can happen with panoramic projections. */ + return vec2(-1.0); + } + return (tmp.xy / tmp.w) * 0.5 + 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General functions handling all projections + * \{ */ + +vec3 camera_view_from_uv(CameraData cam, vec2 uv) +{ + vec3 vV; + switch (cam.type) { + default: + case CAMERA_ORTHO: + case CAMERA_PERSP: + return camera_view_from_uv(cam.wininv, uv); + case CAMERA_PANO_EQUIRECT: + vV = camera_equirectangular_to_direction(cam, uv); + break; + case CAMERA_PANO_EQUIDISTANT: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUISOLID: + vV = camera_fisheye_to_direction(cam, uv); + break; + case CAMERA_PANO_MIRROR: + vV = camera_mirror_ball_to_direction(cam, uv); + break; + } + return vV; +} + +vec2 camera_uv_from_view(CameraData cam, vec3 vV) +{ + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.winmat, false, vV); + case CAMERA_PERSP: + return camera_uv_from_view(cam.winmat, true, vV); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +vec2 camera_uv_from_world(CameraData cam, vec3 V) +{ + vec3 vV = transform_point(cam.viewmat, V); + switch (cam.type) { + default: + case CAMERA_ORTHO: + return camera_uv_from_view(cam.persmat, false, V); + case CAMERA_PERSP: + return camera_uv_from_view(cam.persmat, true, V); + case CAMERA_PANO_EQUIRECT: + return camera_equirectangular_from_direction(cam, vV); + case CAMERA_PANO_EQUISOLID: + /* ATTR_FALLTHROUGH; */ + case CAMERA_PANO_EQUIDISTANT: + return camera_fisheye_from_direction(cam, vV); + case CAMERA_PANO_MIRROR: + return camera_mirror_ball_from_direction(cam, vV); + } +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index 708bd153e84..87154ba6db1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -5,6 +5,7 @@ #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -27,6 +28,19 @@ void main() interp.N = cross(T, interp.curves_binormal); interp.curves_strand_id = hair_get_strand_id(); interp.barycentric_coords = hair_get_barycentric(); +#ifdef MAT_VELOCITY + /* Due to the screen space nature of the vertex positioning, we compute only the motion of curve + * strand, not its cylinder. Otherwise we would add the rotation velocity. */ + int vert_idx = hair_get_base_id(); + vec3 prv, nxt, pos = texelFetch(hairPointBuffer, vert_idx).point_position; + velocity_local_pos_get(pos, vert_idx, prv, nxt); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(prv, pos, nxt, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl index 5b404ec5237..c60527162f7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl @@ -3,6 +3,7 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -38,6 +39,16 @@ void main() aspect, thickness, hardness); +#ifdef MAT_VELOCITY + /* GPencil do not support deformation motion blur. */ + vec3 lP_curr = transform_point(ModelMatrixInverse, interp.P); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(lP_curr, lP_curr, lP_curr, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl index 7b38057f41a..c07a8ae0eea 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -3,6 +3,7 @@ #pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) void main() { @@ -10,6 +11,16 @@ void main() interp.P = point_object_to_world(pos); interp.N = normal_object_to_world(nor); +#ifdef MAT_VELOCITY + vec3 prv, nxt; + velocity_local_pos_get(pos, gl_VertexID, prv, nxt); + /* FIXME(fclem): Evaluating before displacement avoid displacement being treated as motion but + * ignores motion from animated displacement. Supporting animated displacement motion vectors + * would require evaluating the nodetree multiple time with different nodetree UBOs evaluated at + * different times, but also with different attributes (maybe we could assume static attribute at + * least). */ + velocity_vertex(prv, pos, nxt, motion.prev, motion.next); +#endif init_globals(); attrib_load(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index 002eed91130..7ddf941df7c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -8,6 +8,7 @@ #pragma BLENDER_REQUIRE(common_hair_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) /* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */ float hash(vec2 a) @@ -69,4 +70,16 @@ void main() discard; } #endif + +#ifdef MAT_VELOCITY + vec4 out_velocity_camera; /* TODO(fclem): Panoramic cameras. */ + velocity_camera(interp.P + motion.prev, + interp.P, + interp.P - motion.next, + out_velocity_camera, + out_velocity_view); + + /* For testing in viewport. */ + out_velocity_view.zw = vec2(0.0); +#endif } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl new file mode 100644 index 00000000000..435ae6658c9 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl @@ -0,0 +1,101 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) + +#ifdef VELOCITY_CAMERA + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy) + */ +vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next) +{ + vec2 prev_uv, curr_uv, next_uv; + + prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy; + curr_uv = transform_point(ViewProjectionMatrix, P).xy; + next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy; + + vec4 motion; + motion.xy = prev_uv - curr_uv; + motion.zw = curr_uv - next_uv; + /* Convert NDC velocity to UV velocity */ + motion *= 0.5; + + return motion; +} + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy) + * \a velocity_camera is the motion in film UV space after camera projection. + * \a velocity_view is the motion in ShadingView UV space. It is different + * from velocity_camera for multi-view rendering. + */ +void velocity_camera(vec3 P_prev, vec3 P, vec3 P_next, out vec4 vel_camera, out vec4 vel_view) +{ + vec2 prev_uv, curr_uv, next_uv; + prev_uv = camera_uv_from_world(camera_prev, P_prev); + curr_uv = camera_uv_from_world(camera_curr, P); + next_uv = camera_uv_from_world(camera_next, P_next); + + vel_camera.xy = prev_uv - curr_uv; + vel_camera.zw = curr_uv - next_uv; + + if (is_panoramic(camera_curr.type)) { + /* This path is only used if using using panoramic projections. Since the views always have + * the same 45° aperture angle, we can safely reuse the projection matrix. */ + prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy; + curr_uv = transform_point(ViewProjectionMatrix, P).xy; + next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy; + + vel_view.xy = prev_uv - curr_uv; + vel_view.zw = curr_uv - next_uv; + /* Convert NDC velocity to UV velocity */ + vel_view *= 0.5; + } + else { + vel_view = vel_camera; + } +} + +#endif + +#ifdef MAT_VELOCITY + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns a tuple of world space motion deltas. + */ +void velocity_local_pos_get(vec3 lP, int vert_id, out vec3 lP_prev, out vec3 lP_next) +{ + VelocityIndex vel = velocity_indirection_buf[resource_id]; + lP_next = lP_prev = lP; + if (vel.geo.do_deform) { + if (vel.geo.ofs[STEP_PREVIOUS] != -1) { + lP_prev = velocity_geo_prev_buf[vel.geo.ofs[STEP_PREVIOUS] + vert_id].xyz; + } + if (vel.geo.ofs[STEP_NEXT] != -1) { + lP_next = velocity_geo_next_buf[vel.geo.ofs[STEP_NEXT] + vert_id].xyz; + } + } +} + +/** + * Given a triple of position, compute the previous and next motion vectors. + * Returns a tuple of world space motion deltas. + */ +void velocity_vertex( + vec3 lP_prev, vec3 lP, vec3 lP_next, out vec3 motion_prev, out vec3 motion_next) +{ + VelocityIndex vel = velocity_indirection_buf[resource_id]; + mat4 obmat_prev = velocity_obj_prev_buf[vel.obj.ofs[STEP_PREVIOUS]]; + mat4 obmat_next = velocity_obj_next_buf[vel.obj.ofs[STEP_NEXT]]; + vec3 P_prev = transform_point(obmat_prev, lP_prev); + vec3 P_next = transform_point(obmat_next, lP_next); + vec3 P = transform_point(ModelMatrix, lP); + motion_prev = P_prev - P; + motion_next = P_next - P; +} + +#endif diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl new file mode 100644 index 00000000000..b68b2eaf117 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl @@ -0,0 +1,58 @@ + +/** + * Fullscreen pass that compute motion vector for static geometry. + * Animated geometry has already written correct motion vectors. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) + +#define is_valid_output(img_) (imageSize(img_).x > 1) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 motion = imageLoad(velocity_view_img, texel); + + bool pixel_has_valid_motion = (motion.x != VELOCITY_INVALID); + float depth = texelFetch(depth_tx, texel, 0).r; + bool is_background = (depth == 1.0f); + + vec2 uv = vec2(texel) * drw_view.viewport_size_inverse; + vec3 P_next, P_prev, P_curr; + + if (pixel_has_valid_motion) { + /* Animated geometry. View motion already computed during prepass. Convert only to camera. */ + // P_prev = get_world_space_from_depth(uv + motion.xy, 0.5); + // P_curr = get_world_space_from_depth(uv, 0.5); + // P_next = get_world_space_from_depth(uv + motion.zw, 0.5); + return; + } + else if (is_background) { + /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */ + vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0)); + vec3 V = transform_direction(ViewMatrixInverse, vV); + /* Background has no motion under camera translation. Translate view vector with the camera. */ + /* WATCH(fclem): Might create precision issues. */ + P_next = camera_next.viewinv[3].xyz + V; + P_curr = camera_curr.viewinv[3].xyz + V; + P_prev = camera_prev.viewinv[3].xyz + V; + } + else { + /* Static geometry. No translation in world space. */ + P_curr = get_world_space_from_depth(uv, depth); + P_prev = P_curr; + P_next = P_curr; + } + + vec4 vel_camera, vel_view; + velocity_camera(P_prev, P_curr, P_next, vel_camera, vel_view); + + if (in_texture_range(texel, depth_tx)) { + imageStore(velocity_view_img, texel, vel_view); + + if (is_valid_output(velocity_camera_img)) { + imageStore(velocity_camera_img, texel, vel_camera); + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 4d6895bcde0..49250b5741e 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -22,6 +22,7 @@ GPU_SHADER_CREATE_INFO(eevee_sampling_data) * \{ */ GPU_SHADER_CREATE_INFO(eevee_geom_mesh) + .additional_info("eevee_shared") .define("MAT_GEOM_MESH") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") @@ -29,16 +30,19 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh) .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_gpencil) + .additional_info("eevee_shared") .define("MAT_GEOM_GPENCIL") .vertex_source("eevee_geom_gpencil_vert.glsl") .additional_info("draw_gpencil", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_curves) + .additional_info("eevee_shared") .define("MAT_GEOM_CURVES") .vertex_source("eevee_geom_curves_vert.glsl") .additional_info("draw_hair", "draw_resource_id_varying", "draw_resource_handle"); GPU_SHADER_CREATE_INFO(eevee_geom_world) + .additional_info("eevee_shared") .define("MAT_GEOM_WORLD") .builtins(BuiltinBits::VERTEX_ID) .vertex_source("eevee_geom_world_vert.glsl") @@ -66,7 +70,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") GPU_SHADER_CREATE_INFO(eevee_surf_deferred) .vertex_out(eevee_surf_iface) - /* Note: This removes the possibility of using gl_FragDepth. */ + /* NOTE: This removes the possibility of using gl_FragDepth. */ // .early_fragment_test(true) /* Direct output. */ .fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0) @@ -95,7 +99,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .fragment_source("eevee_surf_forward_frag.glsl") // .additional_info("eevee_sampling_data", // "eevee_lightprobe_data", - /* Optionnally added depending on the material. */ + /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", // "eevee_utility_texture", diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh new file mode 100644 index 00000000000..a5f16363466 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh @@ -0,0 +1,55 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Surface Velocity + * + * Combined with the depth prepass shader. + * Outputs the view motion vectors for animated objects. + * \{ */ + +/* Pass world space deltas to the fragment shader. + * This is to make sure that the resulting motion vectors are valid even with displacement. */ +GPU_SHADER_INTERFACE_INFO(eevee_velocity_surface_iface, "motion") + .smooth(Type::VEC3, "prev") + .smooth(Type::VEC3, "next"); + +GPU_SHADER_CREATE_INFO(eevee_velocity_camera) + .define("VELOCITY_CAMERA") + .uniform_buf(1, "CameraData", "camera_prev") + .uniform_buf(2, "CameraData", "camera_curr") + .uniform_buf(3, "CameraData", "camera_next"); + +GPU_SHADER_CREATE_INFO(eevee_velocity_geom) + .define("MAT_VELOCITY") + .auto_resource_location(true) + .storage_buf(4, Qualifier::READ, "mat4", "velocity_obj_prev_buf[]", Frequency::PASS) + .storage_buf(5, Qualifier::READ, "mat4", "velocity_obj_next_buf[]", Frequency::PASS) + .storage_buf(6, Qualifier::READ, "vec4", "velocity_geo_prev_buf[]", Frequency::PASS) + .storage_buf(7, Qualifier::READ, "vec4", "velocity_geo_next_buf[]", Frequency::PASS) + .storage_buf( + 7, Qualifier::READ, "VelocityIndex", "velocity_indirection_buf[]", Frequency::PASS) + .vertex_out(eevee_velocity_surface_iface) + .fragment_out(0, Type::VEC4, "out_velocity_view") + .additional_info("eevee_velocity_camera"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Velocity Resolve + * + * Computes velocity for static objects. + * Also converts motion to camera space (as opposed to view space) if needed. + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_velocity_resolve) + .do_static_compilation(true) + .local_group_size(8, 8) + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_view_img") + .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "velocity_camera_img") + .additional_info("eevee_shared") + .compute_source("eevee_velocity_resolve_comp.glsl") + .additional_info("draw_view", "eevee_velocity_camera"); + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 40ebd262df5..ec44fdf42d5 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -290,7 +290,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, /* Masking: Go through mask list and extract valid masks in a bitmap. */ if (is_masked) { bool valid_mask = false; - /* Warning: only GP_MAX_MASKBITS amount of bits. + /* WARNING: only #GP_MAX_MASKBITS amount of bits. * TODO(fclem): Find a better system without any limitation. */ tgp_layer->mask_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); tgp_layer->mask_invert_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 19afdb3de5a..c7ef8677336 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -61,10 +61,10 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, /* Depth need to be remapped to [0..1] range. */ pix_z = MEM_dupallocN(pix_z); - int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + int pix_num = rpass_z_src->rectx * rpass_z_src->recty; if (DRW_view_is_persp_get(view)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); } @@ -74,7 +74,7 @@ void GPENCIL_render_init(GPENCIL_Data *vedata, float near = DRW_view_near_distance_get(view); float far = DRW_view_far_distance_get(view); float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { pix_z[i] = (pix_z[i] + near) * range_inv; pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); } @@ -172,11 +172,11 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert GPU depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -192,7 +192,7 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index acfd2f98044..4cfe9fcea4e 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -115,7 +115,7 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata) const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); const bool do_edges_only = (ts->uv_flag & UV_SYNC_SELECTION) ? /* NOTE: Ignore #SCE_SELECT_EDGE because a single selected edge - * on the mesh may cause singe UV vertices to be selected. */ + * on the mesh may cause single UV vertices to be selected. */ false : (ts->uv_selectmode == UV_SELECT_EDGE); const bool do_faces = ((sima->flag & SI_NO_DRAWFACES) == 0); diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 5c5226bfe65..9531b0dd983 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -124,7 +124,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) } } - /* Handles and curve point for Curve Edit submode. */ + /* Handles and curve point for Curve Edit sub-mode. */ if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { DRWState state = DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state); @@ -297,7 +297,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - int line_ct = gridlines * 4 + 2; + const int line_count = gridlines * 4 + 2; DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; state |= (grid_xray) ? DRW_STATE_DEPTH_ALWAYS : DRW_STATE_DEPTH_LESS_EQUAL; @@ -311,8 +311,8 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); - DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); - DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_count); } } diff --git a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh index a765d881682..b9b1b73dbd4 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/extra_info.hh @@ -284,7 +284,7 @@ GPU_SHADER_INTERFACE_INFO(overlay_particle_iface, "").flat(Type::VEC4, "finalCol GPU_SHADER_CREATE_INFO(overlay_particle) .sampler(0, ImageType::FLOAT_1D, "weightTex") - .push_constant(Type::VEC4, "color") /* Drawsize packed in alpha */ + .push_constant(Type::VEC4, "color") /* Draw-size packed in alpha. */ .vertex_in(0, Type::VEC3, "part_pos") .vertex_in(1, Type::VEC4, "part_rot") .vertex_in(2, Type::FLOAT, "part_val") diff --git a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh index 713c8c2dc4b..5853e974eeb 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/volume_info.hh @@ -48,7 +48,7 @@ GPU_SHADER_CREATE_INFO(overlay_volume_velocity_needle) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Volume Gridlines +/** \name Volume Grid-Lines * \{ */ GPU_SHADER_INTERFACE_INFO(overlay_volume_gridlines_iface, "").flat(Type::VEC4, "finalColor"); diff --git a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh index 43367121d6a..16b59f6bb7d 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/wireframe_info.hh @@ -21,7 +21,7 @@ GPU_SHADER_CREATE_INFO(overlay_wireframe) .sampler(0, ImageType::DEPTH_2D, "depthTex") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "nor") - .vertex_in(2, Type::FLOAT, "wd") /* wiredata */ + .vertex_in(2, Type::FLOAT, "wd") /* wire-data. */ .vertex_out(overlay_wireframe_iface) .vertex_source("wireframe_vert.glsl") .fragment_source("wireframe_frag.glsl") diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl index bea54ff8cf5..472a589f441 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -347,7 +347,7 @@ void main() line_end = vec2(0.0, 0.5); break; default: - /* Ensure values are assigned to, avoids undefined behaviour for + /* Ensure values are assigned to, avoids undefined behavior for * divergent control-flow. This can occur if discard is called * as discard is not treated as a return in Metal 2.2. So * side-effects can still cause problems. */ diff --git a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh index ad0de61ffc3..e3166582197 100644 --- a/source/blender/draw/engines/select/shaders/infos/select_id_info.hh +++ b/source/blender/draw/engines/select/shaders/infos/select_id_info.hh @@ -3,7 +3,7 @@ #include "gpu_shader_create_info.hh" /* -------------------------------------------------------------------- */ -/** \name Select ID fo Edit Mesh selection +/** \name Select ID for Edit Mesh Selection * \{ */ GPU_SHADER_INTERFACE_INFO(select_id_iface, "").flat(Type::INT, "id"); diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh index e2a54788c72..735e7b6d867 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_prepass_info.hh @@ -91,7 +91,7 @@ GPU_SHADER_CREATE_INFO(workbench_material) * \{ */ GPU_SHADER_CREATE_INFO(workbench_transparent_accum) - /* Note: Blending will be skipped on objectId because output is a + /* NOTE: Blending will be skipped on objectId because output is a * non-normalized integer buffer. */ .fragment_out(0, Type::VEC4, "transparentAccum") .fragment_out(1, Type::VEC4, "revealageAccum") diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 36059b6076f..49e26cd3e0c 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -218,13 +218,13 @@ void main() /* Manual depth test. TODO: remove. */ float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; if (gl_FragCoord.z >= depth) { - /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can - * produce undefined behaviour. This is especially prominent with derivatives if control-flow + /* NOTE: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can + * produce undefined behavior. This is especially prominent with derivatives if control-flow * divergence is present. * - * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing + * Adding a return call eliminates undefined behavior and a later out-of-bounds read causing * a crash on AMD platforms. - * This behaviour can also affect OpenGL on certain devices. */ + * This behavior can also affect OpenGL on certain devices. */ discard; return; } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 1279682e899..e5dcf6c5624 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -115,11 +115,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); - int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); /* Convert ogl depth [0..1] to view Z [near..far] */ if (DRW_view_is_persp_get(NULL)) { - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } @@ -135,7 +135,7 @@ static void workbench_render_result_z(struct RenderLayer *rl, float far = DRW_view_far_distance_get(NULL); float range = fabsf(far - near); - for (int i = 0; i < pix_ct; i++) { + for (int i = 0; i < pix_num; i++) { if (rp->rect[i] == 1.0f) { rp->rect[i] = 1e10f; /* Background */ } diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c902e9b627..ce7773e7439 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -124,12 +124,12 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, double noise_ofs; BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; - float slice_ct[3] = {fds->res[0], fds->res[1], fds->res[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, fds->slice_per_voxel)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); + float slice_count[3] = {fds->res[0], fds->res[1], fds->res[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, fds->slice_per_voxel)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); BKE_object_dimensions_get(ob, dim); - invert_v3(slice_ct); - mul_v3_v3(dim, slice_ct); + invert_v3(slice_count); + mul_v3_v3(dim, slice_count); step_length = len_v3(dim); grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); @@ -273,12 +273,12 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata, float step_length, max_slice; int resolution[3]; GPU_texture_get_mipmap_size(grid->texture, 0, resolution); - float slice_ct[3] = {resolution[0], resolution[1], resolution[2]}; - mul_v3_fl(slice_ct, max_ff(0.001f, 5.0f)); - max_slice = max_fff(slice_ct[0], slice_ct[1], slice_ct[2]); - invert_v3(slice_ct); - mul_v3_v3(slice_ct, world_size); - step_length = len_v3(slice_ct); + float slice_count[3] = {resolution[0], resolution[1], resolution[2]}; + mul_v3_fl(slice_count, max_ff(0.001f, 5.0f)); + max_slice = max_fff(slice_count[0], slice_count[1], slice_count[2]); + invert_v3(slice_count); + mul_v3_v3(slice_count, world_size); + step_length = len_v3(slice_count); /* Set uniforms. */ grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index d7e752a43f4..257f01a5562 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -102,7 +102,7 @@ class DataBuffer { { BLI_STATIC_ASSERT(!device_only, ""); BLI_assert(index >= 0); - BLI_assert(index < len); + BLI_assert(index < len_); return data_[index]; } @@ -110,7 +110,7 @@ class DataBuffer { { BLI_STATIC_ASSERT(!device_only, ""); BLI_assert(index >= 0); - BLI_assert(index < len); + BLI_assert(index < len_); return data_[index]; } @@ -139,7 +139,7 @@ class DataBuffer { const T *end() const { BLI_STATIC_ASSERT(!device_only, ""); - return data_ + len; + return data_ + len_; } T *begin() @@ -150,13 +150,13 @@ class DataBuffer { T *end() { BLI_STATIC_ASSERT(!device_only, ""); - return data_ + len; + return data_ + len_; } operator Span<T>() const { BLI_STATIC_ASSERT(!device_only, ""); - return Span<T>(data_, len); + return Span<T>(data_, len_); } }; @@ -217,7 +217,9 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable if (name) { name_ = name; } - init(len); + this->len_ = len; + constexpr GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); } ~StorageCommon() @@ -225,15 +227,6 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable GPU_storagebuf_free(ssbo_); } - void resize(int64_t new_size) - { - BLI_assert(new_size > 0); - if (new_size != this->len_) { - GPU_storagebuf_free(ssbo_); - this->init(new_size); - } - } - void push_update(void) { BLI_assert(device_only == false); @@ -249,14 +242,6 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable { return &ssbo_; } - - private: - void init(int64_t new_size) - { - this->len_ = new_size; - GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; - ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); - } }; } // namespace detail @@ -333,6 +318,34 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> { { MEM_freeN(this->data_); } + + void resize(int64_t new_size) + { + BLI_assert(new_size > 0); + if (new_size != this->len_) { + /* Manual realloc since MEM_reallocN_aligned does not exists. */ + T *new_data_ = (T *)MEM_mallocN_aligned(new_size * sizeof(T), 16, this->name_); + memcpy(new_data_, this->data_, min_uu(this->len_, new_size) * sizeof(T)); + MEM_freeN(this->data_); + this->data_ = new_data_; + GPU_storagebuf_free(this->ssbo_); + + this->len_ = new_size; + constexpr GPUUsageType usage = device_only ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_DYNAMIC; + this->ssbo_ = GPU_storagebuf_create_ex(sizeof(T) * this->len_, nullptr, usage, this->name_); + } + } + + /* Resize on access. */ + T &get_or_resize(int64_t index) + { + BLI_assert(index >= 0); + if (index >= this->len_) { + size_t size = power_of_2_max_u(index + 1); + this->resize(size); + } + return this->data_[index]; + } }; template< @@ -880,4 +893,56 @@ class Framebuffer : NonCopyable { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Double & Triple buffering util + * + * This is not strictly related to a GPU type and could be moved elsewhere. + * \{ */ + +template<typename T, int64_t len> class SwapChain { + private: + std::array<T, len> chain_; + int64_t index_ = 0; + + public: + void swap() + { + index_ = (index_ + 1) % len; + } + + T ¤t() + { + return chain_[index_]; + } + + T &previous() + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + T &next() + { + return chain_[(index_ + 1) % len]; + } + + const T ¤t() const + { + return chain_[index_]; + } + + const T &previous() const + { + /* Avoid modulo operation with negative numbers. */ + return chain_[(index_ + len - 1) % len]; + } + + const T &next() const + { + return chain_[(index_ + 1) % len]; + } +}; + +/** \} */ + } // namespace blender::draw diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 712118e8282..07105757b2c 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -296,6 +296,8 @@ void DRW_shader_library_free(DRWShaderLibrary *lib); * therefore they aren't ordered as a bit mask. */ typedef enum { + /** To be used for compute passes. */ + DRW_STATE_NO_DRAW = 0, /** Write mask */ DRW_STATE_WRITE_DEPTH = (1 << 0), DRW_STATE_WRITE_COLOR = (1 << 1), @@ -430,12 +432,12 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_ex(shgroup, ob, NULL, geom, true, NULL) void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_num); /** * A count of 0 instance will use the default number of instance in the batch. */ void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct); + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num); void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, @@ -621,6 +623,12 @@ void DRW_shgroup_vertex_buffer_ex(DRWShadingGroup *shgroup, void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, const char *name, struct GPUVertBuf **vertex_buffer DRW_DEBUG_FILE_LINE_ARGS); +void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf *vertex_buffer); +void DRW_shgroup_buffer_texture_ref(DRWShadingGroup *shgroup, + const char *name, + struct GPUVertBuf **vertex_buffer); #ifdef DRW_UNUSED_RESOURCE_TRACKING # define DRW_shgroup_vertex_buffer(shgroup, name, vert) \ @@ -738,6 +746,7 @@ const DRWView *DRW_view_get_active(void); */ void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); void DRW_view_camtexco_set(DRWView *view, float texco[4]); +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]); /* For all getters, if view is NULL, default view is assumed. */ diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc new file mode 100644 index 00000000000..714f1dbb3d1 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.cc @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#include "draw_attributes.h" + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool drw_attributes_has_request(const DRW_Attributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void drw_attributes_merge_requests(const DRW_Attributes *src_requests, + DRW_Attributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (drw_attributes_has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +void drw_attributes_clear(DRW_Attributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_Attributes)); +} + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex) +{ + BLI_mutex_lock(render_mutex); + drw_attributes_merge_requests(src, dst); + BLI_mutex_unlock(render_mutex); +} + +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b) +{ + for (int i = 0; i < b->num_requests; i++) { + if (!drw_attributes_has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return nullptr; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; + return req; +} + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[7] = { + CD_PROP_BOOL, + CD_PROP_INT8, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} diff --git a/source/blender/draw/intern/draw_attributes.h b/source/blender/draw/intern/draw_attributes.h new file mode 100644 index 00000000000..192ffa43337 --- /dev/null +++ b/source/blender/draw/intern/draw_attributes.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Utilities for rendering attributes. + */ + +#pragma once + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute.h" + +#include "BLI_sys_types.h" +#include "BLI_threads.h" + +#include "GPU_shader.h" +#include "GPU_vertex_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; + char attribute_name[64]; +} DRW_AttributeRequest; + +typedef struct DRW_Attributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_Attributes; + +void drw_attributes_clear(DRW_Attributes *attributes); + +void drw_attributes_merge(DRW_Attributes *dst, + const DRW_Attributes *src, + ThreadMutex *render_mutex); + +/* Return true if all requests in b are in a. */ +bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b); + +DRW_AttributeRequest *drw_attributes_add_request(DRW_Attributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain); + +bool drw_custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 67700a4274f..fb074cc728e 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -3400,6 +3400,9 @@ void DRW_batch_cache_free_old(Object *ob, int ctime) case OB_MESH: DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime); break; + case OB_CURVES: + DRW_curves_batch_cache_free_old((Curves *)ob->data, ctime); + break; /* TODO: all cases. */ default: break; diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index cb6006e303a..ce3ad9923da 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -20,6 +20,8 @@ struct TaskGraph; #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" +#include "draw_attributes.h" + /* Vertex Group Selection and display options */ typedef struct DRW_MeshWeightState { int defgroup_active; @@ -67,17 +69,6 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) -typedef struct DRW_AttributeRequest { - CustomDataType cd_type; - int layer_index; - AttributeDomain domain; -} DRW_AttributeRequest; - -typedef struct DRW_MeshAttributes { - DRW_AttributeRequest requests[GPU_MAX_ATTR]; - int num_requests; -} DRW_MeshAttributes; - typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -294,7 +285,7 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; - DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + DRW_Attributes attr_used, attr_needed, attr_used_over_time; int lastmatch; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index ec544d8e786..3d44d3d1b3f 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -793,7 +793,12 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, /* The order in which extractors are added to the list matters somewhat, as some buffers are * reused when building others. */ EXTRACT_ADD_REQUESTED(ibo, tris); - EXTRACT_ADD_REQUESTED(vbo, pos_nor); + + /* Orcos are extracted at the same time as positions. */ + if (DRW_vbo_requested(mbuflist->vbo.pos_nor) || DRW_vbo_requested(mbuflist->vbo.orco)) { + extractors.append(&extract_pos_nor); + } + EXTRACT_ADD_REQUESTED(vbo, lnor); for (int i = 0; i < GPU_MAX_ATTR; i++) { EXTRACT_ADD_REQUESTED(vbo, attr[i]); @@ -843,7 +848,6 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); - EXTRACT_ADD_REQUESTED(vbo, orco); EXTRACT_ADD_REQUESTED(vbo, vcol); EXTRACT_ADD_REQUESTED(vbo, weights); EXTRACT_ADD_REQUESTED(vbo, sculpt_data); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index f877c94208f..0755d5967d5 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -83,6 +83,7 @@ void DRW_batch_cache_free_old(struct Object *ob, int ctime); * \note For now this only free the shading batches / VBO if any cd layers is not needed anymore. */ void DRW_mesh_batch_cache_free_old(struct Mesh *me, int ctime); +void DRW_curves_batch_cache_free_old(struct Curves *curves, int ctime); /** \} */ diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 7b8f34b999c..ebcdabe4942 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -108,7 +108,7 @@ static void curve_eval_render_wire_verts_edges_len_get(const blender::bke::Curve const blender::VArray<bool> cyclic = curves.cyclic(); for (const int i : curves.curves_range()) { const IndexRange points = curves.evaluated_points_for_curve(i); - *r_edge_len += blender::bke::curves::curve_segment_size(points.size(), cyclic[i]); + *r_edge_len += blender::bke::curves::curve_segment_num(points.size(), cyclic[i]); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index f2742f3bcc7..f9cf0021fcd 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -24,6 +24,7 @@ #include "DNA_scene_types.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "GPU_batch.h" #include "GPU_material.h" @@ -31,12 +32,16 @@ #include "DRW_render.h" +#include "draw_attributes.h" #include "draw_cache_impl.h" /* own include */ #include "draw_cache_inline.h" #include "draw_curves_private.h" /* own include */ +#include "draw_shader.h" +using blender::ColorGeometry4f; using blender::float3; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; /* ---------------------------------------------------------------------- */ @@ -49,6 +54,10 @@ struct CurvesBatchCache { /* To determine if cache is invalid. */ bool is_dirty; + + /** Needed when updating material data (e.g. attributes) as the same curves might be used for + * multiple objects with different materials. */ + ThreadMutex render_mutex; }; static bool curves_batch_cache_valid(const Curves &curves) @@ -63,6 +72,7 @@ static void curves_batch_cache_init(Curves &curves) if (!cache) { cache = MEM_cnew<CurvesBatchCache>(__func__); + BLI_mutex_init(&cache->render_mutex); curves.batch_cache = cache; } else { @@ -72,6 +82,23 @@ static void curves_batch_cache_init(Curves &curves) cache->is_dirty = false; } +static void curves_discard_attributes(CurvesEvalCache &curves_cache) +{ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(curves_cache.proc_attributes_tex[i]); + } + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + for (int j = 0; j < GPU_MAX_ATTR; j++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].attributes_buf[j]); + DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].attributes_tex[j]); + } + + drw_attributes_clear(&curves_cache.final[i].attr_used); + } +} + static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) { /* TODO: more granular update tagging. */ @@ -92,6 +119,8 @@ static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]); } } + + curves_discard_attributes(curves_cache); } static void curves_batch_cache_clear(Curves &curves) @@ -138,55 +167,89 @@ void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode) void DRW_curves_batch_cache_free(Curves *curves) { curves_batch_cache_clear(*curves); + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + BLI_mutex_end(&cache->render_mutex); MEM_SAFE_FREE(curves->batch_cache); } +void DRW_curves_batch_cache_free_old(Curves *curves, int ctime) +{ + CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache); + if (cache == nullptr) { + return; + } + + bool do_discard = false; + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + CurvesEvalFinalCache &final_cache = cache->curves_cache.final[i]; + + if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) { + final_cache.last_attr_matching_time = ctime; + } + + if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) { + do_discard = true; + } + + drw_attributes_clear(&final_cache.attr_used_over_time); + } + + if (do_discard) { + curves_discard_attributes(cache->curves_cache); + } +} + static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache) { if (curves_cache.proc_point_buf != nullptr) { return; } - curves_cache.strands_len = curves.geometry.curve_size; - curves_cache.elems_len = curves.geometry.point_size + curves.geometry.curve_size; - curves_cache.point_len = curves.geometry.point_size; + curves_cache.strands_len = curves.geometry.curve_num; + curves_cache.elems_len = curves.geometry.point_num + curves.geometry.curve_num; + curves_cache.point_len = curves.geometry.point_num; } -static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, - GPUVertBufRaw &attr_step, - GPUVertBufRaw &length_step) +struct PositionAndParameter { + float3 position; + float parameter; +}; + +static void curves_batch_cache_fill_segments_proc_pos( + const Curves &curves_id, + MutableSpan<PositionAndParameter> posTime_data, + MutableSpan<float> hairLength_data) { /* TODO: use hair radius layer if available. */ - const int curve_size = curves_id.geometry.curve_size; + const int curve_num = curves_id.geometry.curve_num; const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); Span<float3> positions = curves.positions(); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range = curves.points_for_curve(i); + for (const int i_curve : IndexRange(curve_num)) { + const IndexRange points = curves.points_for_curve(i_curve); + + Span<float3> curve_positions = positions.slice(points); + MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points); - Span<float3> curve_positions = positions.slice(curve_range); float total_len = 0.0f; - float *seg_data_first; - for (const int i_curve : curve_positions.index_range()) { - float *seg_data = (float *)GPU_vertbuf_raw_step(&attr_step); - copy_v3_v3(seg_data, curve_positions[i_curve]); - if (i_curve == 0) { - seg_data_first = seg_data; - } - else { - total_len += blender::math::distance(curve_positions[i_curve - 1], - curve_positions[i_curve]); + for (const int i_point : curve_positions.index_range()) { + if (i_point > 0) { + total_len += blender::math::distance(curve_positions[i_point - 1], + curve_positions[i_point]); } - seg_data[3] = total_len; + curve_posTime_data[i_point].position = curve_positions[i_point]; + curve_posTime_data[i_point].parameter = total_len; } + hairLength_data[i_curve] = total_len; + /* Assign length value. */ - *(float *)GPU_vertbuf_raw_step(&length_step) = total_len; if (total_len > 0.0f) { + const float factor = 1.0f / total_len; /* Divide by total length to have a [0-1] number. */ - for ([[maybe_unused]] const int i_curve : curve_positions.index_range()) { - seg_data_first[3] /= total_len; - seg_data_first += 4; + for (const int i_point : curve_positions.index_range()) { + curve_posTime_data[i_point].parameter *= factor; } } } @@ -199,26 +262,26 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { /* Initialize vertex format. */ GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "pos"); cache.proc_point_buf = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len); - GPUVertBufRaw point_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_point_buf, pos_id, &point_step); + MutableSpan posTime_data{ + reinterpret_cast<PositionAndParameter *>(GPU_vertbuf_get_data(cache.proc_point_buf)), + cache.point_len}; GPUVertFormat length_format = {0}; - uint length_id = GPU_vertformat_attr_add( - &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format); GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len); - GPUVertBufRaw length_step; - GPU_vertbuf_attr_get_raw_data(cache.proc_length_buf, length_id, &length_step); + MutableSpan hairLength_data{ + reinterpret_cast<float *>(GPU_vertbuf_get_data(cache.proc_length_buf)), cache.strands_len}; - curves_batch_cache_fill_segments_proc_pos(curves, point_step, length_step); + curves_batch_cache_fill_segments_proc_pos(curves, posTime_data, hairLength_data); /* Create vbo immediately to bind to texture buffer. */ GPU_vertbuf_use(cache.proc_point_buf); @@ -237,6 +300,88 @@ static void curves_batch_cache_ensure_procedural_pos(Curves &curves, } } +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]) +{ + char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name); +} + +static void curves_batch_cache_ensure_procedural_final_attr( + CurvesEvalCache &cache, GPUVertFormat *format, int subdiv, int index, const char *name) +{ + CurvesEvalFinalCache &final_cache = cache.final[subdiv]; + final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(format, + GPU_USAGE_DEVICE_ONLY); + + /* Create a destination buffer for the transform feedback. Sized appropriately */ + /* Those are points! not line segments. */ + GPU_vertbuf_data_alloc(final_cache.attributes_buf[index], + final_cache.strands_res * cache.strands_len); + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(final_cache.attributes_buf[index]); + + final_cache.attributes_tex[index] = GPU_texture_create_from_vertbuf( + name, final_cache.attributes_buf[index]); +} + +static void curves_batch_ensure_attribute(const Curves &curves, + CurvesEvalCache &cache, + const DRW_AttributeRequest &request, + int subdiv, + int index) +{ + GPU_VERTBUF_DISCARD_SAFE(cache.proc_attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.proc_attributes_tex[index]); + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + /* All attributes use vec4, see comment below. */ + GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format(&format); + GPUVertBuf *attr_vbo = cache.proc_attributes_buf[index]; + + GPU_vertbuf_data_alloc(attr_vbo, + request.domain == ATTR_DOMAIN_POINT ? curves.geometry.point_num : + curves.geometry.curve_num); + + CurveComponent component; + component.replace(const_cast<Curves *>(&curves), GeometryOwnershipType::ReadOnly); + + /* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done + * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following + * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a + * similar texture state swizzle to map the attribute correctly as for volume attributes, so we + * can control the conversion ourselves. */ + blender::VArray<ColorGeometry4f> attribute = component.attribute_get_for_read<ColorGeometry4f>( + request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f}); + + MutableSpan<ColorGeometry4f> vbo_span{ + static_cast<ColorGeometry4f *>(GPU_vertbuf_get_data(attr_vbo)), + component.attribute_domain_num(request.domain)}; + + attribute.materialize(vbo_span); + + GPU_vertbuf_use(attr_vbo); + cache.proc_attributes_tex[index] = GPU_texture_create_from_vertbuf(sampler_name, attr_vbo); + + /* Existing final data may have been for a different attribute (with a different name or domain), + * free the data. */ + GPU_VERTBUF_DISCARD_SAFE(cache.final[subdiv].attributes_buf[index]); + DRW_TEXTURE_FREE_SAFE(cache.final[subdiv].attributes_tex[index]); + + /* Ensure final data for points. */ + if (request.domain == ATTR_DOMAIN_POINT) { + curves_batch_cache_ensure_procedural_final_attr(cache, &format, subdiv, index, sampler_name); + } +} + static void curves_batch_cache_fill_strands_data(const Curves &curves_id, GPUVertBufRaw &data_step, GPUVertBufRaw &seg_step) @@ -307,7 +452,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, const int res, GPUIndexBufBuilder &elb) { - const int curves_num = curves.geometry.curve_size; + const int curves_num = curves.geometry.curve_num; uint curr_point = 0; @@ -353,6 +498,88 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves, prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); } +static bool curves_ensure_attributes(const Curves &curves, + CurvesBatchCache &cache, + GPUMaterial *gpu_material, + int subdiv) +{ + ThreadMutex *render_mutex = &cache.render_mutex; + const CustomData *cd_curve = &curves.geometry.curve_data; + const CustomData *cd_point = &curves.geometry.point_data; + + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + const char *name = gpu_attr->name; + int type = gpu_attr->type; + int layer = -1; + AttributeDomain domain; + + if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) { + domain = ATTR_DOMAIN_CURVE; + } + else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else { + continue; + } + + switch (type) { + default: + break; + case CD_PROP_BOOL: + case CD_PROP_INT8: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1) { + DRW_AttributeRequest *req = drw_attributes_add_request( + &attrs_needed, (CustomDataType)type, layer, domain); + if (req) { + BLI_strncpy(req->attribute_name, name, sizeof(req->attribute_name)); + } + } + break; + } + } + } + + CurvesEvalFinalCache &final_cache = cache.curves_cache.final[subdiv]; + + const bool attr_overlap = drw_attributes_overlap(&final_cache.attr_used, &attrs_needed); + if (attr_overlap == false) { + /* Some new attributes have been added, free all and start over. */ + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(cache.curves_cache.proc_attributes_buf[i]); + DRW_TEXTURE_FREE_SAFE(cache.curves_cache.proc_attributes_tex[i]); + } + drw_attributes_merge(&final_cache.attr_used, &attrs_needed, render_mutex); + } + drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, render_mutex); + + bool need_tf_update = false; + + for (int i = 0; i < final_cache.attr_used.num_requests; i++) { + const DRW_AttributeRequest &request = final_cache.attr_used.requests[i]; + + if (cache.curves_cache.proc_attributes_buf[i] != nullptr) { + continue; + } + + if (request.domain == ATTR_DOMAIN_POINT) { + need_tf_update = true; + } + + curves_batch_ensure_attribute(curves, cache.curves_cache, request, subdiv, i); + } + + return need_tf_update; +} + bool curves_ensure_procedural_data(Object *object, CurvesEvalCache **r_hair_cache, GPUMaterial *gpu_material, @@ -390,6 +617,10 @@ bool curves_ensure_procedural_data(Object *object, curves, cache.curves_cache, thickness_res, subdiv); } + if (gpu_material) { + need_ft_update |= curves_ensure_attributes(curves, cache, gpu_material, subdiv); + } + return need_ft_update; } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index dac4b7488be..a6ab2176d16 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -281,91 +281,6 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } -/** \name DRW_MeshAttributes - * - * Utilities for handling requested attributes. - * \{ */ - -/* Return true if the given DRW_AttributeRequest is already in the requests. */ -static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) -{ - for (int i = 0; i < requests->num_requests; i++) { - const DRW_AttributeRequest src_req = requests->requests[i]; - if (src_req.domain != req.domain) { - continue; - } - if (src_req.layer_index != req.layer_index) { - continue; - } - if (src_req.cd_type != req.cd_type) { - continue; - } - return true; - } - return false; -} - -static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, - DRW_MeshAttributes *dst_requests) -{ - for (int i = 0; i < src_requests->num_requests; i++) { - if (dst_requests->num_requests == GPU_MAX_ATTR) { - return; - } - - if (has_request(dst_requests, src_requests->requests[i])) { - continue; - } - - dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; - dst_requests->num_requests += 1; - } -} - -static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) -{ - memset(attributes, 0, sizeof(DRW_MeshAttributes)); -} - -static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, - const DRW_MeshAttributes *src, - ThreadMutex *mesh_render_mutex) -{ - BLI_mutex_lock(mesh_render_mutex); - mesh_attrs_merge_requests(src, dst); - BLI_mutex_unlock(mesh_render_mutex); -} - -/* Return true if all requests in b are in a. */ -static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) -{ - for (int i = 0; i < b->num_requests; i++) { - if (!has_request(a, b->requests[i])) { - return false; - } - } - - return true; -} - -static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, - CustomDataType type, - int layer, - AttributeDomain domain) -{ - if (attrs->num_requests >= GPU_MAX_ATTR) { - return; - } - - DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; - req->cd_type = type; - req->layer_index = layer; - req->domain = domain; - attrs->num_requests += 1; -} - -/** \} */ - BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -475,36 +390,6 @@ static void mesh_cd_calc_active_mloopcol_layer(const Object *object, } } -static bool custom_data_match_attribute(const CustomData *custom_data, - const char *name, - int *r_layer_index, - int *r_type) -{ - const int possible_attribute_types[7] = { - CD_PROP_BOOL, - CD_PROP_INT8, - CD_PROP_INT32, - CD_PROP_FLOAT, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_COLOR, - }; - - for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { - const int attr_type = possible_attribute_types[i]; - int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); - if (layer_index == -1) { - continue; - } - - *r_layer_index = layer_index; - *r_type = attr_type; - return true; - } - - return false; -} - static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, const CustomData *cd_vdata, const CustomData *cd_ldata, @@ -531,7 +416,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, layer_i; } - /* Note: this is not the same as the layer_i below. */ + /* NOTE: this is not the same as the layer_i below. */ if (layer_i != -1) { layer = (domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata)->layers + layer_i; } @@ -544,7 +429,7 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, return -1; } - /* Note: this is the logical index into the color attribute list, + /* NOTE: this is the logical index into the color attribute list, * not the customdata index. */ int vcol_i = BKE_id_attribute_to_index( (ID *)me_query, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); @@ -556,7 +441,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, const Mesh *me, struct GPUMaterial **gpumat_array, int gpumat_array_len, - DRW_MeshAttributes *attributes) + DRW_Attributes *attributes) { const Mesh *me_final = editmesh_final_or_this(object, me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); @@ -636,16 +521,16 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, if (layer == -1) { /* Try to match a generic attribute, we use the first attribute domain with a * matching name. */ - if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { domain = ATTR_DOMAIN_POINT; } - else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { domain = ATTR_DOMAIN_CORNER; } - else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { domain = ATTR_DOMAIN_FACE; } - else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { domain = ATTR_DOMAIN_EDGE; } else { @@ -701,7 +586,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, break; } - /* Note: attr->type will always be CD_PROP_COLOR even for + /* NOTE: attr->type will always be CD_PROP_COLOR even for * CD_PROP_BYTE_COLOR layers, see node_shader_gpu_vertex_color in * node_shader_vertex_color.cc. */ @@ -718,7 +603,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -729,7 +614,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, case CD_PROP_FLOAT: case CD_PROP_FLOAT2: { if (layer != -1 && domain != ATTR_DOMAIN_NUM) { - drw_mesh_attributes_add_request(attributes, type, layer, domain); + drw_attributes_add_request(attributes, type, layer, domain); } break; } @@ -1317,8 +1202,8 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); + DRW_Attributes attrs_needed; + drw_attributes_clear(&attrs_needed); DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( object, me, gpumat_array, gpumat_array_len, &attrs_needed); @@ -1326,7 +1211,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Object *object, mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1596,7 +1481,7 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } - if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + if (drw_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { cache->lastmatch = ctime; } @@ -1605,12 +1490,12 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) } mesh_cd_layers_type_clear(&cache->cd_used_over_time); - drw_mesh_attributes_clear(&cache->attr_used_over_time); + drw_attributes_clear(&cache->attr_used_over_time); } static void drw_add_attributes_vbo(GPUBatch *batch, MeshBufferList *mbuflist, - DRW_MeshAttributes *attr_used) + DRW_Attributes *attr_used) { for (int i = 0; i < attr_used->num_requests; i++) { DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); @@ -1721,7 +1606,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + bool attr_overlap = drw_attributes_overlap(&cache->attr_used, &cache->attr_needed); if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { @@ -1741,7 +1626,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } - if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + if (!drw_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { for (int i = 0; i < GPU_MAX_ATTR; i++) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); } @@ -1756,13 +1641,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); + drw_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); - drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); - drw_mesh_attributes_clear(&cache->attr_needed); + drw_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index a1c0a42ba6f..c1d609bf648 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -277,7 +277,7 @@ static void particle_calculate_parent_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int parent_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -306,7 +306,7 @@ static void particle_calculate_parent_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int parent_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -337,7 +337,7 @@ static void particle_interpolate_children_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_uv_layers, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (*r_uv)[2]) { if (psmd == NULL) { @@ -361,7 +361,7 @@ static void particle_interpolate_children_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol *r_mcol) { if (psmd == NULL) { @@ -388,7 +388,7 @@ static void particle_calculate_uvs(ParticleSystem *psys, const int num_uv_layers, const int parent_index, const int child_index, - /*const*/ MTFace **mtfaces, + const MTFace **mtfaces, float (**r_parent_uvs)[2], float (**r_uv)[2]) { @@ -431,7 +431,7 @@ static void particle_calculate_mcol(ParticleSystem *psys, const int num_col_layers, const int parent_index, const int child_index, - /*const*/ MCol **mcols, + const MCol **mcols, MCol **r_parent_mcol, MCol **r_mcol) { @@ -482,8 +482,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys, const int num_path_keys, const int num_uv_layers, const int num_col_layers, - /*const*/ MTFace **mtfaces, - /*const*/ MCol **mcols, + const MTFace **mtfaces, + const MCol **mcols, uint *uv_id, uint *col_id, float (***r_parent_uvs)[2], @@ -713,11 +713,11 @@ static int particle_batch_cache_fill_strands_data(ParticleSystem *psys, GPUVertBufRaw *seg_step, float (***r_parent_uvs)[2], GPUVertBufRaw *uv_step, - MTFace **mtfaces, + const MTFace **mtfaces, int num_uv_layers, MCol ***r_parent_mcol, GPUVertBufRaw *col_step, - MCol **mcols, + const MCol **mcols, int num_col_layers) { const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES); @@ -834,8 +834,8 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPUVertBufRaw uv_step[MAX_MTFACE]; GPUVertBufRaw col_step[MAX_MCOL]; - MTFace *mtfaces[MAX_MTFACE] = {NULL}; - MCol *mcols[MAX_MCOL] = {NULL}; + const MTFace *mtfaces[MAX_MTFACE] = {NULL}; + const MCol *mcols[MAX_MCOL] = {NULL}; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -909,12 +909,13 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit BKE_mesh_tessface_ensure(psmd->mesh_final); if (cache->num_uv_layers) { for (int j = 0; j < cache->num_uv_layers; j++) { - mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j); + mtfaces[j] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, j); } } if (cache->num_col_layers) { for (int j = 0; j < cache->num_col_layers; j++) { - mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); + mcols[j] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j); } } } @@ -930,11 +931,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } else { @@ -951,11 +952,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } if (psys->childcache) { @@ -970,11 +971,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit &seg_step, &parent_uvs, uv_step, - (MTFace **)mtfaces, + mtfaces, cache->num_uv_layers, &parent_mcol, col_step, - (MCol **)mcols, + mcols, cache->num_col_layers); } } @@ -1147,8 +1148,8 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, int num_col_layers = 0; int active_uv = 0; int active_col = 0; - MTFace **mtfaces = NULL; - MCol **mcols = NULL; + const MTFace **mtfaces = NULL; + const MCol **mcols = NULL; float(**parent_uvs)[2] = NULL; MCol **parent_mcol = NULL; @@ -1214,13 +1215,14 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, if (num_uv_layers) { mtfaces = MEM_mallocN(sizeof(*mtfaces) * num_uv_layers, "Faces UV layers"); for (int i = 0; i < num_uv_layers; i++) { - mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, i); + mtfaces[i] = (const MTFace *)CustomData_get_layer_n( + &psmd->mesh_final->fdata, CD_MTFACE, i); } } if (num_col_layers) { mcols = MEM_mallocN(sizeof(*mcols) * num_col_layers, "Color layers"); for (int i = 0; i < num_col_layers; i++) { - mcols[i] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); + mcols[i] = (const MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i); } } } @@ -1304,10 +1306,10 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, MEM_freeN(parent_mcol); } if (num_uv_layers) { - MEM_freeN(mtfaces); + MEM_freeN((void *)mtfaces); } if (num_col_layers) { - MEM_freeN(mcols); + MEM_freeN((void *)mcols); } if (psmd != NULL) { MEM_freeN(uv_id); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 9241ef67a55..52170fbd518 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -70,6 +70,7 @@ enum { SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS, SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS, + SHADER_PATCH_EVALUATION_ORCO, SHADER_COMP_CUSTOM_DATA_INTERP_1D, SHADER_COMP_CUSTOM_DATA_INTERP_2D, SHADER_COMP_CUSTOM_DATA_INTERP_3D, @@ -109,7 +110,8 @@ static const char *get_shader_code(int shader_type) case SHADER_PATCH_EVALUATION: case SHADER_PATCH_EVALUATION_FVAR: case SHADER_PATCH_EVALUATION_FACE_DOTS: - case SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS: { + case SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS: + case SHADER_PATCH_EVALUATION_ORCO: { return datatoc_common_subdiv_patch_evaluation_comp_glsl; } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: @@ -168,6 +170,9 @@ static const char *get_shader_name(int shader_type) case SHADER_PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS: { return "subdiv patch evaluation face dots with normals"; } + case SHADER_PATCH_EVALUATION_ORCO: { + return "subdiv patch evaluation orco"; + } case SHADER_COMP_CUSTOM_DATA_INTERP_1D: { return "subdiv custom data interp 1D"; } @@ -218,6 +223,12 @@ static GPUShader *get_patch_evaluation_shader(int shader_type) "#define FDOTS_EVALUATION\n" "#define FOTS_NORMALS\n"; } + else if (shader_type == SHADER_PATCH_EVALUATION_ORCO) { + defines = + "#define OSD_PATCH_BASIS_GLSL\n" + "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" + "#define ORCO_EVALUATION\n"; + } else { defines = "#define OSD_PATCH_BASIS_GLSL\n" @@ -248,7 +259,8 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines) if (ELEM(shader_type, SHADER_PATCH_EVALUATION, SHADER_PATCH_EVALUATION_FVAR, - SHADER_PATCH_EVALUATION_FACE_DOTS)) { + SHADER_PATCH_EVALUATION_FACE_DOTS, + SHADER_PATCH_EVALUATION_ORCO)) { return get_patch_evaluation_shader(shader_type); } if (g_subdiv_shaders[shader_type] == nullptr) { @@ -731,6 +743,23 @@ static DRWSubdivCache *mesh_batch_cache_ensure_subdiv_cache(MeshBatchCache *mbc) return subdiv_cache; } +static void draw_subdiv_invalidate_evaluator_for_orco(Subdiv *subdiv, Mesh *mesh) +{ + const bool has_orco = CustomData_has_layer(&mesh->vdata, CD_ORCO); + if (has_orco && subdiv->evaluator && !subdiv->evaluator->hasVertexData(subdiv->evaluator)) { + /* If we suddenly have/need original coordinates, recreate the evaluator if the extra + * source was not created yet. The refiner also has to be recreated as refinement for source + * and vertex data is done only once. */ + openSubdiv_deleteEvaluator(subdiv->evaluator); + subdiv->evaluator = nullptr; + + if (subdiv->topology_refiner != nullptr) { + openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner); + subdiv->topology_refiner = nullptr; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -766,8 +795,8 @@ struct DRWCacheBuildingContext { /* Origindex layers from the mesh to directly look up during traversal the origindex from the * base mesh for edit data so that we do not have to handle yet another GPU buffer and do this in * the shaders. */ - int *v_origindex; - int *e_origindex; + const int *v_origindex; + const int *e_origindex; }; static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_context, @@ -1276,7 +1305,9 @@ 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) +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + GPUVertBuf *pos_nor, + GPUVertBuf *orco) { if (!draw_subdiv_cache_need_polygon_data(cache)) { /* Happens on meshes with only loose geometry. */ @@ -1291,6 +1322,14 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_subdiv_vertex_format()); evaluator->wrapSrcBuffer(evaluator, &src_buffer_interface); + GPUVertBuf *src_extra_buffer = nullptr; + if (orco) { + OpenSubdiv_Buffer src_extra_buffer_interface; + src_extra_buffer = create_buffer_and_interface(&src_extra_buffer_interface, + get_subdiv_vertex_format()); + evaluator->wrapSrcVertexDataBuffer(evaluator, &src_extra_buffer_interface); + } + OpenSubdiv_Buffer patch_arrays_buffer_interface; GPUVertBuf *patch_arrays_buffer = create_buffer_and_interface(&patch_arrays_buffer_interface, get_patch_array_format()); @@ -1306,7 +1345,8 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); + GPUShader *shader = get_patch_evaluation_shader(orco ? SHADER_PATCH_EVALUATION_ORCO : + SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); int binding_point = 0; @@ -1319,6 +1359,10 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++); GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); + if (orco) { + GPU_vertbuf_bind_as_ssbo(src_extra_buffer, binding_point++); + GPU_vertbuf_bind_as_ssbo(orco, binding_point++); + } BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads); @@ -1335,6 +1379,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no GPU_vertbuf_discard(patch_param_buffer); GPU_vertbuf_discard(patch_arrays_buffer); GPU_vertbuf_discard(src_buffer); + GPU_VERTBUF_DISCARD_SAFE(src_extra_buffer); } void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, @@ -1990,6 +2035,8 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, return false; } + draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval); + if (!BKE_subdiv_eval_begin_from_mesh( subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) { /* This could happen in two situations: diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 779ac43178c..b6b0c94f4bf 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -13,6 +13,7 @@ extern "C" { #endif +struct CurvesUniformBufPool; struct DRWShadingGroup; struct FluidModifierData; struct GPUMaterial; @@ -44,7 +45,7 @@ float *DRW_color_background_blend_get(int theme_id); bool DRW_object_is_flat(struct Object *ob, int *r_axis); bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); -/* draw_hair.c */ +/* draw_hair.cc */ /** * This creates a shading group with display hairs. @@ -82,7 +83,8 @@ struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); -void DRW_curves_init(void); +void DRW_curves_init(struct DRWData *drw_data); +void DRW_curves_ubos_pool_free(struct CurvesUniformBufPool *pool); void DRW_curves_update(void); void DRW_curves_free(void); diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc index 88118361115..39d4845994f 100644 --- a/source/blender/draw/intern/draw_curves.cc +++ b/source/blender/draw/intern/draw_curves.cc @@ -10,6 +10,7 @@ #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "DNA_curves_types.h" #include "DNA_customdata_types.h" #include "GPU_batch.h" @@ -20,9 +21,13 @@ #include "GPU_texture.h" #include "GPU_vertex_buffer.h" +#include "DRW_gpu_wrapper.hh" #include "DRW_render.h" +#include "draw_cache_impl.h" +#include "draw_curves_private.h" #include "draw_hair_private.h" +#include "draw_manager.h" #include "draw_shader.h" #ifndef __APPLE__ @@ -61,16 +66,43 @@ static GPUVertBuf *g_dummy_vbo = nullptr; static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ +using CurvesInfosBuf = blender::draw::UniformBuffer<CurvesInfos>; + +struct CurvesUniformBufPool { + blender::Vector<std::unique_ptr<CurvesInfosBuf>> ubos; + int used = 0; + + void reset() + { + used = 0; + } + + CurvesInfosBuf &alloc() + { + if (used >= ubos.size()) { + ubos.append(std::make_unique<CurvesInfosBuf>()); + return *ubos.last(); + } + return *ubos[used++]; + } +}; + static GPUShader *curves_eval_shader_get(CurvesEvalShader type) { return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get()); } -void DRW_curves_init(void) +void DRW_curves_init(DRWData *drw_data) { /* Initialize legacy hair too, to avoid verbosity in callers. */ DRW_hair_init(); + if (drw_data->curves_ubos == nullptr) { + drw_data->curves_ubos = MEM_new<CurvesUniformBufPool>("CurvesUniformBufPool"); + } + CurvesUniformBufPool *pool = drw_data->curves_ubos; + pool->reset(); + #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0); #else @@ -94,63 +126,120 @@ void DRW_curves_init(void) } } +void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool) +{ + MEM_delete(pool); +} + static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, CurvesEvalCache *cache, + GPUTexture *tex, const int subdiv) { - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex); + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex); DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex); DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1); } +static void drw_curves_cache_update_compute(CurvesEvalCache *cache, + const int subdiv, + const int strands_len, + GPUVertBuf *buffer, + GPUTexture *tex) +{ + GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); + drw_curves_cache_shgrp_attach_resources(shgrp, cache, tex, subdiv); + DRW_shgroup_vertex_buffer(shgrp, "posTime", buffer); + + const int max_strands_per_call = GPU_max_work_group_count(0); + int strands_start = 0; + while (strands_start < strands_len) { + int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); + DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); + DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); + DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); + strands_start += batch_strands_len; + } +} + static void drw_curves_cache_update_compute(CurvesEvalCache *cache, const int subdiv) { const int strands_len = cache->strands_len; const int final_points_len = cache->final[subdiv].strands_res * strands_len; - if (final_points_len > 0) { - GPUShader *shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); - DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass); - drw_curves_cache_shgrp_attach_resources(shgrp, cache, subdiv); - DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf); - - const int max_strands_per_call = GPU_max_work_group_count(0); - int strands_start = 0; - while (strands_start < strands_len) { - int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call); - DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp); - DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start); - DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1); - strands_start += batch_strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_compute( + cache, subdiv, strands_len, cache->final[subdiv].proc_buf, cache->point_tex); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; } + + drw_curves_cache_update_compute(cache, + subdiv, + strands_len, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i]); } } -static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, + GPUVertBuf *vbo, + GPUTexture *tex, + const int subdiv, + const int final_points_len) { - const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; - if (final_points_len > 0) { - GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); + GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM); #ifdef USE_TRANSFORM_FEEDBACK - DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create( - tf_shader, g_tf_pass, cache->final[subdiv].proc_buf); + DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo); #else - DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - - CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); - pr_call->next = g_tf_calls; - pr_call->vbo = cache->final[subdiv].proc_buf; - pr_call->shgrp = tf_shgrp; - pr_call->vert_len = final_points_len; - g_tf_calls = pr_call; - DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); - DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); - DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); + DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); + + CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__); + pr_call->next = g_tf_calls; + pr_call->vbo = vbo; + pr_call->shgrp = tf_shgrp; + pr_call->vert_len = final_points_len; + g_tf_calls = pr_call; + DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1); + DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1); + DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1); #endif - drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); + drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, tex, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); +} + +static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache, const int subdiv) +{ + const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len; + if (final_points_len == 0) { + return; + } + + drw_curves_cache_update_transform_feedback( + cache, cache->final[subdiv].proc_buf, cache->point_tex, subdiv, final_points_len); + + const DRW_Attributes &attrs = cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + /* Only refine point attributes. */ + if (attrs.requests[i].domain == ATTR_DOMAIN_CURVE) { + continue; + } + + drw_curves_cache_update_transform_feedback(cache, + cache->final[subdiv].attributes_buf[i], + cache->proc_attributes_tex[i], + subdiv, + final_points_len); } } @@ -186,12 +275,34 @@ GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) return cache->final[subdiv].proc_buf; } +static int attribute_index_in_material(GPUMaterial *gpu_material, const char *name) +{ + if (!gpu_material) { + return -1; + } + + int index = 0; + + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + if (STREQ(gpu_attr->name, name)) { + return index; + } + + index++; + } + + return -1; +} + DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRWShadingGroup *shgrp_parent, GPUMaterial *gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; + CurvesUniformBufPool *pool = DST.vmempool->curves_ubos; + CurvesInfosBuf &curves_infos = pool->alloc(); int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; @@ -209,7 +320,7 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; + float hair_rad_shape = 0.0f; float hair_rad_root = 0.005f; float hair_rad_tip = 0.0f; bool hair_close_tip = true; @@ -218,6 +329,43 @@ DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, if (curves_cache->length_tex) { DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); } + + const DRW_Attributes &attrs = curves_cache->final[subdiv].attr_used; + for (int i = 0; i < attrs.num_requests; i++) { + const DRW_AttributeRequest &request = attrs.requests[i]; + + char sampler_name[32]; + drw_curves_get_attribute_sampler_name(request.attribute_name, sampler_name); + + if (request.domain == ATTR_DOMAIN_CURVE) { + if (!curves_cache->proc_attributes_tex[i]) { + continue; + } + + DRW_shgroup_uniform_texture(shgrp, sampler_name, curves_cache->proc_attributes_tex[i]); + } + else { + if (!curves_cache->final[subdiv].attributes_tex[i]) { + continue; + } + DRW_shgroup_uniform_texture( + shgrp, sampler_name, curves_cache->final[subdiv].attributes_tex[i]); + } + + /* Some attributes may not be used in the shader anymore and were not garbage collected yet, so + * we need to find the right index for this attribute as uniforms defining the scope of the + * attributes are based on attribute loading order, which is itself based on the material's + * attributes. */ + const int index = attribute_index_in_material(gpu_material, request.attribute_name); + if (index != -1) { + curves_infos.is_point_attribute[index] = request.domain == ATTR_DOMAIN_POINT; + } + } + + curves_infos.push_update(); + + DRW_shgroup_uniform_block(shgrp, "drw_curves", curves_infos); + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h index 76d5f15319d..ed4dd50dfbe 100644 --- a/source/blender/draw/intern/draw_curves_private.h +++ b/source/blender/draw/intern/draw_curves_private.h @@ -7,6 +7,11 @@ #pragma once +#include "BKE_attribute.h" +#include "GPU_shader.h" + +#include "draw_attributes.h" + #ifdef __cplusplus extern "C" { #endif @@ -35,6 +40,23 @@ typedef struct CurvesEvalFinalCache { /* Points per curve, at least 2. */ int strands_res; + + /* Attributes currently being or about to be drawn. */ + DRW_Attributes attr_used; + + /* Attributes which were used at some point. This is used for garbage collection, to remove + * attributes which are not used in shaders anymore due to user edits. */ + DRW_Attributes attr_used_over_time; + + /* Last time, in seconds, the `attr_used` and `attr_used_over_time` were exactly the same. + * If the delta between this time and the current scene time is greater than the timeout set in + * user preferences (`U.vbotimeout`) then garbage collection is performed. */ + int last_attr_matching_time; + + /* Output of the subdivision stage: vertex buffers sized to subdiv level. This is only attributes + * on point domain. */ + GPUVertBuf *attributes_buf[GPU_MAX_ATTR]; + GPUTexture *attributes_tex[GPU_MAX_ATTR]; } CurvesEvalFinalCache; /* Curves procedural display: Evaluation is done on the GPU. */ @@ -56,6 +78,11 @@ typedef struct CurvesEvalCache { CurvesEvalFinalCache final[MAX_HAIR_SUBDIV]; + /* For point attributes, which need subdivision, these are the input data. + * For spline attributes, which need not subdivision, these are the final data. */ + GPUVertBuf *proc_attributes_buf[GPU_MAX_ATTR]; + GPUTexture *proc_attributes_tex[GPU_MAX_ATTR]; + int strands_len; int elems_len; int point_len; @@ -70,6 +97,8 @@ bool curves_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); +void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32]); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.cc index 8351452769d..0a3c16e0d71 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.cc @@ -35,7 +35,7 @@ # define USE_COMPUTE_SHADERS #endif -BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) +BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get() { #ifdef USE_COMPUTE_SHADERS if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { @@ -49,21 +49,21 @@ BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get(void) } #ifndef USE_TRANSFORM_FEEDBACK -typedef struct ParticleRefineCall { +struct ParticleRefineCall { struct ParticleRefineCall *next; GPUVertBuf *vbo; DRWShadingGroup *shgrp; uint vert_len; -} ParticleRefineCall; +}; -static ParticleRefineCall *g_tf_calls = NULL; +static ParticleRefineCall *g_tf_calls = nullptr; static int g_tf_id_offset; static int g_tf_target_width; static int g_tf_target_height; #endif -static GPUVertBuf *g_dummy_vbo = NULL; -static GPUTexture *g_dummy_texture = NULL; +static GPUVertBuf *g_dummy_vbo = nullptr; +static GPUTexture *g_dummy_texture = nullptr; static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) @@ -74,12 +74,12 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement) void DRW_hair_init(void) { #if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) - g_tf_pass = DRW_pass_create("Update Hair Pass", 0); + g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW); #else g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR); #endif - if (g_dummy_vbo == NULL) { + if (g_dummy_vbo == nullptr) { /* initialize vertex format */ GPUVertFormat format = {0}; uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); @@ -141,7 +141,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #else DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass); - ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__); + ParticleRefineCall *pr_call = (ParticleRefineCall *)MEM_mallocN(sizeof(*pr_call), __func__); pr_call->next = g_tf_calls; pr_call->vbo = cache->final[subdiv].proc_buf; pr_call->shgrp = tf_shgrp; @@ -153,7 +153,7 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache #endif drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); - DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); } } @@ -188,7 +188,7 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *cache = drw_hair_particle_cache_get( - object, psys, md, NULL, subdiv, thickness_res); + object, psys, md, nullptr, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -201,11 +201,11 @@ void DRW_hair_duplimat_get(Object *object, Object *dupli_parent = DRW_object_get_dupli_parent(object); DupliObject *dupli_object = DRW_object_get_dupli(object); - if ((dupli_parent != NULL) && (dupli_object != NULL)) { + if ((dupli_parent != nullptr) && (dupli_object != nullptr)) { if (dupli_object->type & OB_DUPLICOLLECTION) { unit_m4(dupli_mat); Collection *collection = dupli_parent->instance_collection; - if (collection != NULL) { + if (collection != nullptr) { sub_v3_v3(dupli_mat[3], collection->instance_offset); } mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat); @@ -291,7 +291,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } -void DRW_hair_update(void) +void DRW_hair_update() { #ifndef USE_TRANSFORM_FEEDBACK /** @@ -304,7 +304,7 @@ void DRW_hair_update(void) * and the most local workaround that still uses the power of the GPU. */ - if (g_tf_calls == NULL) { + if (g_tf_calls == nullptr) { return; } @@ -319,21 +319,22 @@ void DRW_hair_update(void) * Do chunks of maximum 2048 * 2048 hair points. */ int width = 2048; int height = min_ii(width, 1 + max_size / width); - GPUTexture *tex = DRW_texture_pool_query_2d(width, height, GPU_RGBA32F, (void *)DRW_hair_update); + GPUTexture *tex = DRW_texture_pool_query_2d( + width, height, GPU_RGBA32F, (DrawEngineType *)DRW_hair_update); g_tf_target_height = height; g_tf_target_width = width; - GPUFrameBuffer *fb = NULL; + GPUFrameBuffer *fb = nullptr; GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(tex), }); - float *data = MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); + float *data = (float *)MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"); GPU_framebuffer_bind(fb); - while (g_tf_calls != NULL) { + while (g_tf_calls != nullptr) { ParticleRefineCall *pr_call = g_tf_calls; g_tf_calls = g_tf_calls->next; @@ -342,7 +343,7 @@ void DRW_hair_update(void) int max_read_px_len = min_ii(width * height, pr_call->vert_len); DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp); - /* Readback result to main memory. */ + /* Read back result to main memory. */ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data); /* Upload back to VBO. */ GPU_vertbuf_use(pr_call->vbo); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4bbcf6eaf42..bc9d0a3d02a 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -44,7 +44,6 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_world_types.h" -#include "draw_manager.h" #include "ED_gpencil.h" #include "ED_screen.h" @@ -479,6 +478,7 @@ void DRW_viewport_data_free(DRWData *drw_data) MEM_freeN(drw_data->obinfos_ubo); } DRW_volume_ubos_pool_free(drw_data->volume_grids_ubos); + DRW_curves_ubos_pool_free(drw_data->curves_ubos); MEM_freeN(drw_data); } @@ -501,7 +501,7 @@ static DRWData *drw_viewport_data_ensure(GPUViewport *viewport) * - size can be NULL to get it from viewport. * - if viewport and size are NULL, size is set to (1, 1). * - * Important: drw_manager_init can be called multiple times before drw_manager_exit. + * IMPORTANT: #drw_manager_init can be called multiple times before #drw_manager_exit. */ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int size[2]) { @@ -529,7 +529,7 @@ static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int s dst->view_data_active = dst->vmempool->view_data[view]; dst->resource_handle = 0; dst->pass_handle = 0; - dst->primary_view_ct = 0; + dst->primary_view_num = 0; drw_viewport_data_reset(dst->vmempool); @@ -1650,7 +1650,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_globals_update(); drw_debug_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2022,7 +2022,7 @@ void DRW_render_object_iter( void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph)) { const DRWContextState *draw_ctx = DRW_context_state_get(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2079,7 +2079,7 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_init(&DST, NULL, NULL); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2114,7 +2114,7 @@ void DRW_cache_restart(void) DST.buffer_finish_called = false; - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); } @@ -2433,7 +2433,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); @@ -2607,7 +2607,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_curves_init(); + DRW_curves_init(DST.vmempool); DRW_volume_init(DST.vmempool); DRW_smoke_init(DST.vmempool); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 7a9585262ff..6d384c599d8 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -319,6 +319,8 @@ typedef enum { DRW_UNIFORM_STORAGE_BLOCK, DRW_UNIFORM_STORAGE_BLOCK_REF, DRW_UNIFORM_TFEEDBACK_TARGET, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, + DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, /** Per drawcall uniforms/UBO */ @@ -536,6 +538,8 @@ typedef struct DRWData { struct DRWTexturePool *texture_pool; /** Per stereo view data. Contains engine data and default framebuffers. */ struct DRWViewData *view_data[2]; + /** Per draw-call curves object data. */ + struct CurvesUniformBufPool *curves_ubos; } DRWData; /* ------------- DRAW MANAGER ------------ */ @@ -617,7 +621,7 @@ typedef struct DRWManager { DRWView *view_default; DRWView *view_active; DRWView *view_previous; - uint primary_view_ct; + uint primary_view_num; /** TODO(@fclem): Remove this. Only here to support * shaders without common_view_lib.glsl */ ViewInfos view_storage_cpy; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index b5432da0957..f0960c5324b 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -547,6 +547,29 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF, vertex_buffer, 0, 0, 1); } +void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf *vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE, vertex_buffer, 0, 0, 1); +} + +void DRW_shgroup_buffer_texture_ref(DRWShadingGroup *shgroup, + const char *name, + GPUVertBuf **vertex_buffer) +{ + int location = GPU_shader_get_ssbo(shgroup->shader, name); + if (location == -1) { + return; + } + drw_shgroup_uniform_create_ex( + shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF, vertex_buffer, 0, 0, 1); +} /** \} */ /* -------------------------------------------------------------------- */ @@ -937,25 +960,25 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } void DRW_shgroup_call_range( - DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_num); } void DRW_shgroup_call_instance_range( - DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_num) { BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_num); } void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, @@ -1905,8 +1928,8 @@ DRWView *DRW_view_create(const float viewmat[4][4], { DRWView *view = BLI_memblock_alloc(DST.vmempool->views); - if (DST.primary_view_ct < MAX_CULLED_VIEWS) { - view->culling_mask = 1u << DST.primary_view_ct++; + if (DST.primary_view_num < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_num++; } else { BLI_assert(0); @@ -2058,6 +2081,11 @@ void DRW_view_camtexco_set(DRWView *view, float texco[4]) copy_v4_v4(view->storage.viewcamtexcofac, texco); } +void DRW_view_camtexco_get(const DRWView *view, float r_texco[4]) +{ + copy_v4_v4(r_texco, view->storage.viewcamtexcofac); +} + void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners) { memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners)); diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 2c5b02f88a9..e7e0e0ce41f 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -693,6 +693,12 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, ((GPUVertBuf *)uni->pvalue)); break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE_REF: + GPU_vertbuf_bind_as_texture(*uni->vertbuf_ref, uni->location); + break; + case DRW_UNIFORM_VERTEX_BUFFER_AS_TEXTURE: + GPU_vertbuf_bind_as_texture(uni->vertbuf, uni->location); + break; case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE_REF: GPU_vertbuf_bind_as_ssbo(*uni->vertbuf_ref, uni->location); break; diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.cc index 063aec24b94..001ceb0ae8d 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.cc @@ -16,15 +16,15 @@ #include "draw_shader.h" -extern char datatoc_common_hair_lib_glsl[]; +extern "C" char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_hair_refine_vert_glsl[]; -extern char datatoc_common_hair_refine_comp_glsl[]; -extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; +extern "C" char datatoc_common_hair_refine_vert_glsl[]; +extern "C" char datatoc_common_hair_refine_comp_glsl[]; +extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; -} e_data = {{NULL}}; +} e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ /** \name Hair refinement @@ -38,19 +38,19 @@ static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED( static GPUShader *hair_refine_shader_transform_feedback_create( ParticleRefineShader UNUSED(refinement)) { - GPUShader *sh = NULL; + GPUShader *sh = nullptr; + + std::string shader_src = std::string(datatoc_common_hair_lib_glsl) + + std::string(datatoc_common_hair_refine_vert_glsl); - char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl, - datatoc_common_hair_refine_vert_glsl); const char *var_names[1] = {"finalColor"}; - sh = DRW_shader_create_with_transform_feedback(shader_src, - NULL, + sh = DRW_shader_create_with_transform_feedback(shader_src.c_str(), + nullptr, "#define HAIR_PHASE_SUBDIV\n" "#define USE_TF\n", GPU_SHADER_TFB_POINTS, var_names, 1); - MEM_freeN(shader_src); return sh; } @@ -64,8 +64,8 @@ static GPUShader *hair_refine_shader_transform_feedback_workaround_create( GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type) { - if (e_data.hair_refine_sh[refinement] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[refinement] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(refinement); @@ -88,8 +88,8 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type) { /* TODO: Implement curves evaluation types (Bezier and Catmull Rom). */ - if (e_data.hair_refine_sh[type] == NULL) { - GPUShader *sh = NULL; + if (e_data.hair_refine_sh[type] == nullptr) { + GPUShader *sh = nullptr; switch (sh_type) { case PART_REFINE_SHADER_COMPUTE: sh = hair_refine_shader_compute_create(PART_REFINE_CATMULL_ROM); @@ -111,7 +111,7 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh /** \} */ -void DRW_shaders_free(void) +void DRW_shaders_free() { for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 650e78c9362..63d755cc334 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -22,7 +22,7 @@ typedef enum eParticleRefineShaderType { PART_REFINE_SHADER_COMPUTE, } eParticleRefineShaderType; -/* draw_shader.c */ +/* draw_shader.cc */ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index db128fffde7..94c0c53dab7 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GPU_SHADER +# include "GPU_shader.h" # include "GPU_shader_shared_utils.h" typedef struct ViewInfos ViewInfos; typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; typedef struct VolumeInfos VolumeInfos; +typedef struct CurvesInfos CurvesInfos; #endif #define DRW_SHADER_SHARED_H @@ -16,6 +18,10 @@ typedef struct VolumeInfos VolumeInfos; /* Define the maximum number of grid we allow in a volume UBO. */ #define DRW_GRID_PER_VOLUME_MAX 16 +/* Define the maximum number of attribute we allow in a curves UBO. + * This should be kept in sync with `GPU_ATTR_MAX` */ +#define DRW_ATTRIBUTE_PER_CURVES_MAX 15 + struct ViewInfos { /* View matrices */ float4x4 persmat; @@ -79,6 +85,14 @@ struct VolumeInfos { }; BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 16) +struct CurvesInfos { + /* Per attribute scope, follows loading order. + * NOTE: uint as bool in GLSL is 4 bytes. */ + uint is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX]; + int _pad; +}; +BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16) + #define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) #define ObjectInfo (drw_infos[resource_id].drw_Infos) #define ObjectColor (drw_infos[resource_id].drw_ObjectColor) diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 2689159d259..15c770a51c4 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -160,8 +160,8 @@ typedef struct DRWSubdivCache { /* Contains the start loop index and the smooth flag for each coarse polygon. */ struct GPUVertBuf *extra_coarse_face_data; - /* Computed for ibo.points, one value per subdivided vertex, mapping coarse vertices -> - * subdivided loop */ + /* Computed for `ibo.points`, one value per subdivided vertex, + * mapping coarse vertices -> subdivided loop. */ int *point_indices; /* Material offsets. */ @@ -235,7 +235,9 @@ 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); +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, + struct GPUVertBuf *pos_nor, + struct GPUVertBuf *orco); void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, struct GPUVertBuf *src_data, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d55386dfd7d..b88cd9e77d2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -67,7 +67,7 @@ typedef struct MeshRenderData { const float (*bm_poly_normals)[3]; const float (*bm_poly_centers)[3]; - int *v_origindex, *e_origindex, *p_origindex; + const int *v_origindex, *e_origindex, *p_origindex; int edge_crease_ofs; int vert_crease_ofs; int bweight_ofs; 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 497cde9aaf2..d595abc6dd3 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 @@ -14,6 +14,7 @@ #include "BKE_attribute.h" +#include "draw_attributes.h" #include "draw_subdivision.h" #include "extract_mesh.h" @@ -180,7 +181,7 @@ static void fill_vertbuf_with_attribute(const MeshRenderData *mr, const MPoly *mpoly = mr->mpoly; const MLoop *mloop = mr->mloop; - const AttributeType *attr_data = static_cast<AttributeType *>( + const AttributeType *attr_data = static_cast<const AttributeType *>( CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); using converter = attribute_type_converter<AttributeType, VBOType>; @@ -284,7 +285,7 @@ static void extract_attr_init(const MeshRenderData *mr, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); @@ -337,7 +338,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, void *UNUSED(tls_data), int index) { - const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_Attributes *attrs_used = &cache->attr_used; const DRW_AttributeRequest &request = attrs_used->requests[index]; Mesh *coarse_mesh = subdiv_cache->mesh; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index 4ced14ab11a..fb4b95885fc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -26,7 +26,7 @@ struct UVStretchAngle { struct MeshExtract_StretchAngle_Data { UVStretchAngle *vbo_data; - MLoopUV *luv; + const MLoopUV *luv; float auv[2][2], last_auv[2]; float av[2][3], last_av[3]; int cd_ofs; @@ -98,7 +98,7 @@ static void extract_edituv_stretch_angle_init(const MeshRenderData *mr, } else { BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH)); - data->luv = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->luv = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } @@ -236,7 +236,7 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache *subdi draw_subdiv_get_pos_nor_format(), subdiv_cache->num_subdiv_loops + loose_geom.loop_len); - draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor); + draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor, nullptr); } /* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 26f0b07f676..e7a3cb03903 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -17,7 +17,7 @@ namespace blender::draw { struct MeshExtract_FdotUV_Data { float (*vbo_data)[2]; - MLoopUV *uv_data; + const MLoopUV *uv_data; int cd_ofs; }; @@ -49,7 +49,7 @@ static void extract_fdots_uv_init(const MeshRenderData *mr, data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV); } else { - data->uv_data = (MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); + data->uv_data = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index ed1a0ccd178..94674a54f12 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -7,8 +7,6 @@ #include "extract_mesh.h" -#include "draw_subdivision.h" - namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -17,7 +15,7 @@ namespace blender::draw { struct MeshExtract_Orco_Data { float (*vbo_data)[4]; - float (*orco)[3]; + const float (*orco)[3]; }; static void extract_orco_init(const MeshRenderData *mr, @@ -42,7 +40,7 @@ static void extract_orco_init(const MeshRenderData *mr, MeshExtract_Orco_Data *data = static_cast<MeshExtract_Orco_Data *>(tls_data); data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo); - data->orco = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); + data->orco = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); /* Make sure `orco` layer was requested only if needed! */ BLI_assert(data->orco); } @@ -79,77 +77,12 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *mr, - struct MeshBatchCache *UNUSED(cache), - void *buffer, - void *UNUSED(data)) -{ - static GPUVertFormat format = {0}; - if (format.attr_len == 0) { - /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex - * attributes. This is a substantial waste of video-ram and should be done another way. - * Unfortunately, at the time of writing, I did not found any other "non disruptive" - * alternative. */ - GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); - GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); - - GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); - /* Dynamic as we upload and interpolate layers one at a time. */ - GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC); - GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len); - - float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo)); - - CustomData *cd_vdata = &mr->me->vdata; - float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO)); - - if (mr->extract_type == MR_EXTRACT_MESH) { - const MLoop *mloop = mr->mloop; - const MPoly *mp = mr->mpoly; - - int ml_index = 0; - for (int i = 0; i < mr->poly_len; i++, mp++) { - const MLoop *ml = &mloop[mp->loopstart]; - - for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) { - float *loop_orco = coarse_vbo_data[ml_index]; - copy_v3_v3(loop_orco, orco[ml->v]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } - } - } - else { - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter; - BMLoop *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - const int l_index = BM_elem_index_get(l_iter); - float *loop_orco = coarse_vbo_data[l_index]; - copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]); - loop_orco[3] = 0.0; /* Tag as not a generic attribute. */ - } while ((l_iter = l_iter->next) != l_first); - } - } - - draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false); - - GPU_vertbuf_discard(coarse_vbo); -} - constexpr MeshExtract create_extractor_orco() { MeshExtract extractor = {nullptr}; extractor.init = extract_orco_init; extractor.iter_poly_bm = extract_orco_iter_poly_bm; extractor.iter_poly_mesh = extract_orco_iter_poly_mesh; - extractor.init_subdiv = extract_orco_init_subdiv; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(MeshExtract_Orco_Data); extractor.use_threading = true; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index ea46d9c4caa..f80b33e28f2 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 @@ -201,7 +201,7 @@ static GPUVertFormat *get_custom_normals_format() static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, const MeshRenderData *UNUSED(mr), - struct MeshBatchCache *UNUSED(cache), + struct MeshBatchCache *cache, void *buffer, void *UNUSED(data)) { @@ -216,11 +216,25 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, return; } - draw_subdiv_extract_pos_nor(subdiv_cache, vbo); + GPUVertBuf *orco_vbo = cache->final.buff.vbo.orco; + + if (orco_vbo) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex + * attributes. This is a substantial waste of video-ram and should be done another way. + * Unfortunately, at the time of writing, I did not found any other "non disruptive" + * alternative. */ + GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + GPU_vertbuf_init_build_on_device(orco_vbo, &format, subdiv_cache->num_subdiv_loops); + } + + draw_subdiv_extract_pos_nor(subdiv_cache, vbo, orco_vbo); if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; - float(*lnors)[3] = static_cast<float(*)[3]>( + const float(*lnors)[3] = static_cast<float(*)[3]>( CustomData_get_layer(&coarse_mesh->ldata, CD_NORMAL)); BLI_assert(lnors != nullptr); 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 96595df9276..5658ed85c8b 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 @@ -42,8 +42,8 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPU_vertbuf_init_with_format(vbo, format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -54,7 +54,7 @@ static void extract_sculpt_data_init(const MeshRenderData *mr, }; gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); + const MLoop *loops = (const MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); if (mr->extract_type == MR_EXTRACT_BMESH) { int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK); @@ -126,7 +126,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, /* First, interpolate mask if available. */ GPUVertBuf *mask_vbo = nullptr; GPUVertBuf *subdiv_mask_vbo = nullptr; - float *cd_mask = (float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); + const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK); if (cd_mask) { GPUVertFormat mask_format = {0}; @@ -167,7 +167,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, }; gpuFaceSet *face_sets = (gpuFaceSet *)GPU_vertbuf_get_data(face_set_vbo); - int *cd_face_set = (int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); + const int *cd_face_set = (const int *)CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS); GPUVertFormat *format = get_sculpt_data_format(); GPU_vertbuf_init_build_on_device(vbo, format, subdiv_cache->num_subdiv_loops); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index 2a4a6a186be..f4c54b2f881 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -188,7 +188,7 @@ static void extract_vert_idx_init_subdiv(const DRWSubdivCache *subdiv_cache, { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom; - /* Each element points to an element in the ibo.points. */ + /* Each element points to an element in the `ibo.points`. */ draw_subdiv_init_origindex_buffer( vbo, (int32_t *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index), 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 25f78d68914..91cd675d32f 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 @@ -291,7 +291,8 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, 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); + const 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; @@ -306,7 +307,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, } 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); + const 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; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 5fb4b401ae3..2808a0a3a71 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -108,7 +108,8 @@ static void extract_uv_init(const MeshRenderData *mr, } } else { - MLoopUV *layer_data = (MLoopUV *)CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i); + const MLoopUV *layer_data = (const MLoopUV *)CustomData_get_layer_n( + cd_ldata, CD_MLOOPUV, i); for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) { memcpy(uv_data, layer_data->uv, sizeof(*uv_data)); } diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 0460ba56e4c..a8931292064 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -110,7 +110,7 @@ layout(std140) uniform globalsBlock vec4 screenVecs[2]; vec4 sizeViewport; /* Inverted size in zw. */ - float sizePixel; /* This one is for dpi scaling */ + float sizePixel; /* This one is for DPI scaling. */ float pixelFac; /* To use with mul_project_m4_v3_zfac() */ float sizeObjectCenter; float sizeLightCenter; diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index ff52b483d77..dfa2f307800 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -212,8 +212,8 @@ void hair_get_pos_tan_binor_time(bool is_persp, wpos += wbinor * thick_time * scale; } else { - /* Note: Ensures 'hairThickTime' is initialised - - * avoids undefined behaviour on certain macOS configurations. */ + /* NOTE: Ensures 'hairThickTime' is initialized - + * avoids undefined behavior on certain macOS configurations. */ thick_time = 0.0; } } 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 e842a73b8b3..0ffb216fc6f 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 @@ -91,6 +91,16 @@ layout(std430, binding = 8) writeonly buffer outputVertexData { PosNorLoop output_verts[]; }; +# if defined(ORCO_EVALUATION) +layout(std430, binding = 9) buffer src_extra_buffer +{ + float srcExtraVertexBuffer[]; +}; +layout(std430, binding = 10) writeonly buffer outputOrcoData +{ + vec4 output_orcos[]; +}; +# endif #endif vec2 read_vec2(int index) @@ -110,6 +120,17 @@ vec3 read_vec3(int index) return result; } +#if defined(ORCO_EVALUATION) +vec3 read_vec3_extra(int index) +{ + vec3 result; + result.x = srcExtraVertexBuffer[index * 3]; + result.y = srcExtraVertexBuffer[index * 3 + 1]; + result.z = srcExtraVertexBuffer[index * 3 + 2]; + return result; +} +#endif + OsdPatchArray GetPatchArray(int arrayIndex) { return patchArrayBuffer[arrayIndex]; @@ -292,6 +313,31 @@ void evaluate_patches_limits( dv += src_vertex * wDv[cv]; } } + +# if defined(ORCO_EVALUATION) +/* Evaluate the patches limits from the extra source vertex buffer. */ +void evaluate_patches_limits_extra(int patch_index, float u, float v, inout vec3 dst) +{ + OsdPatchCoord coord = GetPatchCoord(patch_index, u, v); + OsdPatchArray array = GetPatchArray(coord.arrayIndex); + OsdPatchParam param = GetPatchParam(coord.patchIndex); + + int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc; + + float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20]; + int nPoints = OsdEvaluatePatchBasis( + patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv); + + int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase); + + for (int cv = 0; cv < nPoints; ++cv) { + int index = patchIndexBuffer[indexBase + cv]; + vec3 src_vertex = read_vec3_extra(index); + + dst += src_vertex * wP[cv]; + } +} +# endif #endif /* ------------------------------------------------------------------------------ @@ -422,6 +468,16 @@ void main() set_vertex_pos(vertex_data, pos); set_vertex_nor(vertex_data, nor, flag); output_verts[loop_index] = vertex_data; + +# if defined(ORCO_EVALUATION) + pos = vec3(0.0); + evaluate_patches_limits_extra(patch_co.patch_index, uv.x, uv.y, pos); + + /* Set w = 0.0 to indicate that this is not a generic attribute. + * See comments in `extract_mesh_vbo_orco.cc`. */ + vec4 orco_data = vec4(pos, 0.0); + output_orcos[loop_index] = orco_data; +# endif } } #endif diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh index c74a043ec97..8fd55ea351f 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -10,3 +10,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos) GPU_SHADER_CREATE_INFO(draw_volume_infos) .typedef_source("draw_shader_shared.h") .uniform_buf(2, "VolumeInfos", "drw_volume", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_curves_infos) + .typedef_source("draw_shader_shared.h") + .uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH); diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 7bb1d652bf1..c2d517588b2 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4020,6 +4020,8 @@ static bool acf_nlaaction_setting_valid(bAnimContext *UNUSED(ac), else { return false; } + case ACHANNEL_SETTING_SELECT: /* selected */ + return true; /* unsupported */ default: @@ -4040,6 +4042,9 @@ static int acf_nlaaction_setting_flag(bAnimContext *UNUSED(ac), *neg = true; /* XXX */ return ADT_NLA_EDIT_NOMAP; + case ACHANNEL_SETTING_SELECT: /* selected */ + return ADT_UI_SELECTED; + default: /* unsupported */ return 0; } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 31d90c8bfec..f148bb5b77d 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -125,6 +125,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { @@ -182,6 +183,7 @@ void ANIM_set_active_channel(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale && ale->adt) { @@ -199,7 +201,6 @@ void ANIM_set_active_channel(bAnimContext *ac, /* unhandled currently, but may be interesting */ case ANIMTYPE_MASKLAYER: case ANIMTYPE_SHAPEKEY: - case ANIMTYPE_NLAACTION: break; /* other types */ @@ -312,6 +313,7 @@ static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListB case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) { return ACHANNEL_SETFLAG_CLEAR; @@ -420,6 +422,7 @@ static void anim_channels_select_set(bAnimContext *ac, case ANIMTYPE_DSHAIR: case ANIMTYPE_DSPOINTCLOUD: case ANIMTYPE_DSVOLUME: + case ANIMTYPE_NLAACTION: case ANIMTYPE_DSSIMULATION: { /* need to verify that this data is valid for now */ if (ale->adt) { diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 1a3ab100768..8519b2061f2 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -543,6 +543,9 @@ void ED_markers_draw(const bContext *C, int flag) View2D *v2d = UI_view2d_fromcontext(C); int cfra = CTX_data_scene(C)->r.cfra; + const float line_width = GPU_line_width_get(); + GPU_line_width(1.0f); + rctf markers_region_rect; get_marker_region_rect(v2d, &markers_region_rect); @@ -575,6 +578,7 @@ void ED_markers_draw(const bContext *C, int flag) } } + GPU_line_width(line_width); GPU_matrix_pop(); } diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 5b0c5eac11b..84f99ec0ac0 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -38,7 +38,6 @@ #include "SEQ_iterator.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" -#include "SEQ_transform.h" #include "anim_intern.h" @@ -112,9 +111,9 @@ static int seq_frame_apply_snap(bContext *C, Scene *scene, const int timeline_fr Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { seq_frame_snap_update_best( - SEQ_transform_get_left_handle_frame(seq), timeline_frame, &best_frame, &best_distance); + SEQ_time_left_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance); seq_frame_snap_update_best( - SEQ_transform_get_right_handle_frame(seq), timeline_frame, &best_frame, &best_distance); + SEQ_time_right_handle_frame_get(seq), timeline_frame, &best_frame, &best_distance); } SEQ_collection_free(strips); diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 58d093c678d..786204a52ed 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -168,11 +168,11 @@ void draw_keyframe_shape(float x, /* Common attributes shared between the draw calls. */ typedef struct DrawKeylistUIData { float alpha; - float icon_sz; - float half_icon_sz; - float smaller_sz; - float ipo_sz; - float gpencil_sz; + float icon_size; + float half_icon_size; + float smaller_size; + float ipo_size; + float gpencil_size; float screenspace_margin; float sel_color[4]; float unsel_color[4]; @@ -195,11 +195,11 @@ static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, /* TODO: allow this opacity factor to be themed? */ ctx->alpha = channel_locked ? 0.25f : 1.0f; - ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; - ctx->half_icon_sz = 0.5f * ctx->icon_sz; - ctx->smaller_sz = 0.35f * ctx->icon_sz; - ctx->ipo_sz = 0.1f * ctx->icon_sz; - ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->icon_size = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_size = 0.5f * ctx->icon_size; + ctx->smaller_size = 0.35f * ctx->icon_size; + ctx->ipo_size = 0.1f * ctx->icon_size; + ctx->gpencil_size = ctx->smaller_size * 0.8f; ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; @@ -242,8 +242,8 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), - .ymin = ypos - ctx->gpencil_sz, - .ymax = ypos + ctx->gpencil_sz, + .ymin = ypos - ctx->gpencil_size, + .ymax = ypos + ctx->gpencil_size, }, true, 0.25f * (float)UI_UNIT_X, @@ -259,8 +259,8 @@ static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->smaller_sz, - .ymax = ypos + ctx->smaller_sz, + .ymin = ypos - ctx->smaller_size, + .ymax = ypos + ctx->smaller_size, }, true, 3.0f, @@ -275,8 +275,8 @@ static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->half_icon_sz, - .ymax = ypos + ctx->half_icon_sz, + .ymin = ypos - ctx->half_icon_size, + .ymax = ypos + ctx->half_icon_size, }, true, 3.0f, @@ -291,8 +291,8 @@ static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, &(const rctf){ .xmin = ab->cfra, .xmax = ab->next->cfra, - .ymin = ypos - ctx->ipo_sz, - .ymax = ypos + ctx->ipo_sz, + .ymin = ypos - ctx->ipo_size, + .ymax = ypos + ctx->ipo_size, }, true, 3.0f, @@ -367,7 +367,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx, draw_keyframe_shape(ak->cfra, ypos, - ctx->icon_sz, + ctx->icon_size, (ak->sel & SELECT), ak->key_type, KEYFRAME_SHAPE_BOTH, diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 14a3b958ea6..941125b9ad5 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2051,6 +2051,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -2246,6 +2248,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + RNA_def_property_string_search_func_runtime( + prop, ANIM_keyingset_visit_for_search_no_poll, PROP_STRING_SEARCH_SUGGESTION); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 6fcdd21bad8..97b81277008 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -708,6 +708,72 @@ KeyingSet *ANIM_get_keyingset_for_autokeying(const Scene *scene, const char *tra return ANIM_builtin_keyingset_get_named(NULL, transformKSName); } +static void anim_keyingset_visit_for_search_impl(const bContext *C, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data, + const bool use_poll) +{ + /* Poll requires context. */ + if (use_poll && (C == NULL)) { + return; + } + + Scene *scene = C ? CTX_data_scene(C) : NULL; + KeyingSet *ks; + + /* Active Keying Set. */ + if (!use_poll || (scene && scene->active_keyingset)) { + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = "__ACTIVE__"; + visit_params.info = "Active Keying Set"; + visit_fn(visit_user_data, &visit_params); + } + + /* User-defined Keying Sets. */ + if (scene && scene->keyingsets.first) { + for (ks = scene->keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } + } + + /* Builtin Keying Sets. */ + for (ks = builtin_keyingsets.first; ks; ks = ks->next) { + if (use_poll && !ANIM_keyingset_context_ok_poll((bContext *)C, ks)) { + continue; + } + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = ks->idname; + visit_params.info = ks->name; + visit_fn(visit_user_data, &visit_params); + } +} + +void ANIM_keyingset_visit_for_search(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, false); +} + +void ANIM_keyingset_visit_for_search_no_poll(const bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + anim_keyingset_visit_for_search_impl(C, visit_fn, visit_user_data, true); +} + /* Menu of All Keying Sets ----------------------------- */ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 963d7ea1149..3c445f46902 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -229,7 +229,7 @@ typedef enum eCalcRollTypes { } eCalcRollTypes; static const EnumPropertyItem prop_calc_roll_types[] = { - {0, "", 0, N_("Positive"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Positive"), NULL), {CALC_ROLL_TAN_POS_X, "POS_X", 0, "Local +X Tangent", ""}, {CALC_ROLL_TAN_POS_Z, "POS_Z", 0, "Local +Z Tangent", ""}, @@ -237,8 +237,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = { {CALC_ROLL_POS_Y, "GLOBAL_POS_Y", 0, "Global +Y Axis", ""}, {CALC_ROLL_POS_Z, "GLOBAL_POS_Z", 0, "Global +Z Axis", ""}, - {0, "", 0, N_("Negative"), ""}, - + RNA_ENUM_ITEM_HEADING(N_("Negative"), NULL), {CALC_ROLL_TAN_NEG_X, "NEG_X", 0, "Local -X Tangent", ""}, {CALC_ROLL_TAN_NEG_Z, "NEG_Z", 0, "Local -Z Tangent", ""}, @@ -246,7 +245,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = { {CALC_ROLL_NEG_Y, "GLOBAL_NEG_Y", 0, "Global -Y Axis", ""}, {CALC_ROLL_NEG_Z, "GLOBAL_NEG_Z", 0, "Global -Z Axis", ""}, - {0, "", 0, N_("Other"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Other"), NULL), {CALC_ROLL_ACTIVE, "ACTIVE", 0, "Active Bone", ""}, {CALC_ROLL_VIEW, "VIEW", 0, "View Axis", ""}, {CALC_ROLL_CURSOR, "CURSOR", 0, "Cursor", ""}, diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index 64035732a39..3a1e7419d3c 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -9,6 +9,7 @@ #include "DNA_armature_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index b21ae305dbe..791e28de694 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -23,9 +23,9 @@ set(SRC editcurve.c editcurve_add.c editcurve_paint.c + editcurve_pen.c editcurve_query.c editcurve_select.c - editcurve_pen.c editcurve_undo.c editfont.c editfont_undo.c diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 1731d224b3e..a5d8390e7f2 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -11,6 +11,9 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) set(SRC @@ -24,3 +27,4 @@ set(LIB ) blender_add_lib(bf_editor_curves "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") +add_dependencies(bf_editor_curves bf_rna) diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 7d9e663c444..552ef1d96c8 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -20,7 +20,7 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi MutableSpan<float3> positions = curves.positions_for_write(); float *radius_data = (float *)CustomData_add_layer_named( - &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius"); MutableSpan<float> radii{radius_data, curves.points_num()}; for (const int i : offsets.index_range()) { diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 7d07c211542..efa4f884e17 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -6,20 +6,26 @@ #include <atomic> +#include "BLI_devirtualize_parameters.hh" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "ED_curves.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "WM_api.h" #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_layer.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -32,9 +38,12 @@ #include "DNA_scene_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" +#include "RNA_prototypes.h" /** * The code below uses a suffix naming convention to indicate the coordinate space: @@ -46,6 +55,41 @@ namespace blender::ed::curves { +static bool object_has_editable_curves(const Main &bmain, const Object &object) +{ + if (object.type != OB_CURVES) { + return false; + } + if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) { + return false; + } + if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) { + return false; + } + return true; +} + +static VectorSet<Curves *> get_unique_editable_curves(const bContext &C) +{ + VectorSet<Curves *> unique_curves; + + const Main &bmain = *CTX_data_main(&C); + + Object *object = CTX_data_active_object(&C); + if (object && object_has_editable_curves(bmain, *object)) { + unique_curves.add_new(static_cast<Curves *>(object->data)); + } + + CTX_DATA_BEGIN (&C, Object *, object, selected_objects) { + if (object_has_editable_curves(bmain, *object)) { + unique_curves.add(static_cast<Curves *>(object->data)); + } + } + CTX_DATA_END; + + return unique_curves; +} + using bke::CurvesGeometry; namespace convert_to_particle_system { @@ -192,7 +236,7 @@ static void try_convert_single_object(Object &curves_ob, /* Prepare utility data structure to map hair roots to mfaces. */ const Span<int> mface_to_poly_map{ - static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), + static_cast<const int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)), surface_me.totface}; Array<Vector<int>> poly_to_mface_map(surface_me.totpoly); for (const int mface_i : mface_to_poly_map.index_range()) { @@ -315,6 +359,140 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } +namespace convert_from_particle_system { + +static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys) +{ + ParticleSettings &settings = *psys.part; + if (psys.part->type != PART_HAIR) { + return {}; + } + + const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0; + + const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached}; + const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache}; + + int points_num = 0; + Vector<int> curve_offsets; + Vector<int> parents_to_transfer; + Vector<int> children_to_transfer; + if (transfer_parents) { + for (const int parent_i : parents_cache.index_range()) { + const int segments = parents_cache[parent_i]->segments; + if (segments <= 0) { + continue; + } + parents_to_transfer.append(parent_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + } + for (const int child_i : children_cache.index_range()) { + const int segments = children_cache[child_i]->segments; + if (segments <= 0) { + continue; + } + children_to_transfer.append(child_i); + curve_offsets.append(points_num); + points_num += segments + 1; + } + const int curves_num = parents_to_transfer.size() + children_to_transfer.size(); + curve_offsets.append(points_num); + BLI_assert(curve_offsets.size() == curves_num + 1); + bke::CurvesGeometry curves(points_num, curves_num); + curves.offsets_for_write().copy_from(curve_offsets); + + const float4x4 object_to_world_mat = object.obmat; + const float4x4 world_to_object_mat = object_to_world_mat.inverted(); + + MutableSpan<float3> positions = curves.positions_for_write(); + + const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache, + const Span<int> indices_to_transfer, + const int curve_index_offset) { + threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) { + for (const int i : range) { + const int hair_i = indices_to_transfer[i]; + const int curve_i = i + curve_index_offset; + const IndexRange points = curves.points_for_curve(curve_i); + const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()}; + for (const int key_i : keys.index_range()) { + const float3 key_pos_wo = keys[key_i].co; + positions[points[key_i]] = world_to_object_mat * key_pos_wo; + } + } + }); + }; + + if (transfer_parents) { + copy_hair_to_curves(parents_cache, parents_to_transfer, 0); + } + copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size()); + + curves.update_curve_types(); + curves.tag_topology_changed(); + return curves; +} + +static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main &bmain = *CTX_data_main(C); + ViewLayer &view_layer = *CTX_data_view_layer(C); + Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C); + Object *ob_from_orig = ED_object_active_context(C); + ParticleSystem *psys_orig = static_cast<ParticleSystem *>( + CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data); + if (psys_orig == nullptr) { + psys_orig = psys_get_current(ob_from_orig); + } + if (psys_orig == nullptr) { + return OPERATOR_CANCELLED; + } + Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig); + ParticleSystem *psys_eval = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md); + if (!STREQ(psmd->psys->name, psys_orig->name)) { + continue; + } + psys_eval = psmd->psys; + } + + Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name); + ob_new->dtx |= OB_DRAWBOUNDOX; /* TODO: Remove once there is actual drawing. */ + Curves *curves_id = static_cast<Curves *>(ob_new->data); + BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false); + bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval); + + DEG_relations_tag_update(&bmain); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + + return OPERATOR_FINISHED; +} + +static bool curves_convert_from_particle_system_poll(bContext *C) +{ + return ED_object_active_context(C) != nullptr; +} + +} // namespace convert_from_particle_system + +static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot) +{ + ot->name = "Convert Particle System to Curves"; + ot->idname = "CURVES_OT_convert_from_particle_system"; + ot->description = "Add a new curves object based on the current state of the particle system"; + + ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll; + ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + namespace snap_curves_to_surface { enum class AttachMode { @@ -508,11 +686,220 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) "How to find the point on the surface to attach to"); } +static bool selection_poll(bContext *C) +{ + const Object *object = CTX_data_active_object(C); + if (object == nullptr) { + return false; + } + if (object->type != OB_CURVES) { + return false; + } + if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) { + return false; + } + return true; +} + +namespace set_selection_domain { + +static int curves_set_selection_domain_exec(bContext *C, wmOperator *op) +{ + const AttributeDomain domain = AttributeDomain(RNA_enum_get(op->ptr, "domain")); + + for (Curves *curves_id : get_unique_editable_curves(*C)) { + if (curves_id->selection_domain == domain && (curves_id->flag & CV_SCULPT_SELECTION_ENABLED)) { + continue; + } + + const AttributeDomain old_domain = AttributeDomain(curves_id->selection_domain); + curves_id->selection_domain = domain; + curves_id->flag |= CV_SCULPT_SELECTION_ENABLED; + + CurveComponent component; + component.replace(curves_id, GeometryOwnershipType::Editable); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + + if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) { + VArray<float> curve_selection = curves.adapt_domain( + curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + curve_selection.materialize(curves.selection_curve_float_for_write()); + component.attribute_try_delete(".selection_point_float"); + } + else if (old_domain == ATTR_DOMAIN_CURVE && domain == ATTR_DOMAIN_POINT) { + VArray<float> point_selection = curves.adapt_domain( + curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + point_selection.materialize(curves.selection_point_float_for_write()); + component.attribute_try_delete(".selection_curve_float"); + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); + + return OPERATOR_FINISHED; +} + +} // namespace set_selection_domain + +static void CURVES_OT_set_selection_domain(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Set Select Mode"; + ot->idname = __func__; + ot->description = "Change the mode used for selection masking in curves sculpt mode"; + + ot->exec = set_selection_domain::curves_set_selection_domain_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = prop = RNA_def_enum( + ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", ""); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); +} + +namespace disable_selection { + +static int curves_disable_selection_exec(bContext *C, wmOperator *UNUSED(op)) +{ + for (Curves *curves_id : get_unique_editable_curves(*C)) { + curves_id->flag &= ~CV_SCULPT_SELECTION_ENABLED; + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); + + return OPERATOR_FINISHED; +} + +} // namespace disable_selection + +static void CURVES_OT_disable_selection(wmOperatorType *ot) +{ + ot->name = "Disable Selection"; + ot->idname = __func__; + ot->description = "Disable the drawing of influence of selection in sculpt mode"; + + ot->exec = disable_selection::curves_disable_selection_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +namespace select_all { + +static bool varray_contains_nonzero(const VArray<float> &data) +{ + bool contains_nonzero = false; + devirtualize_varray(data, [&](const auto array) { + for (const int i : data.index_range()) { + if (array[i] != 0.0f) { + contains_nonzero = true; + break; + } + } + }); + return contains_nonzero; +} + +static bool any_point_selected(const CurvesGeometry &curves) +{ + return varray_contains_nonzero(curves.selection_point_float()); +} + +static bool any_point_selected(const Span<Curves *> curves_ids) +{ + for (const Curves *curves_id : curves_ids) { + if (any_point_selected(CurvesGeometry::wrap(curves_id->geometry))) { + return true; + } + } + return false; +} + +static void invert_selection(MutableSpan<float> selection) +{ + threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + selection[i] = 1.0f - selection[i]; + } + }); +} + +static int select_all_exec(bContext *C, wmOperator *op) +{ + int action = RNA_enum_get(op->ptr, "action"); + + VectorSet<Curves *> unique_curves = get_unique_editable_curves(*C); + + if (action == SEL_TOGGLE) { + action = any_point_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT; + } + + for (Curves *curves_id : unique_curves) { + if (action == SEL_SELECT) { + CurveComponent component; + component.replace(curves_id, GeometryOwnershipType::Editable); + component.attribute_try_delete(".selection_point_float"); + component.attribute_try_delete(".selection_curve_float"); + } + else { + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); + MutableSpan<float> selection = curves_id->selection_domain == ATTR_DOMAIN_POINT ? + curves.selection_point_float_for_write() : + curves.selection_curve_float_for_write(); + if (action == SEL_DESELECT) { + selection.fill(0.0f); + } + else if (action == SEL_INVERT) { + invert_selection(selection); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic + * attribute for now. */ + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + + return OPERATOR_FINISHED; +} + +} // namespace select_all + +static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot) +{ + ot->name = "(De)select All"; + ot->idname = __func__; + ot->description = "(De)select all control points"; + + ot->exec = select_all::select_all_exec; + ot->poll = selection_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + } // namespace blender::ed::curves void ED_operatortypes_curves() { using namespace blender::ed::curves; WM_operatortype_append(CURVES_OT_convert_to_particle_system); + WM_operatortype_append(CURVES_OT_convert_from_particle_system); WM_operatortype_append(CURVES_OT_snap_curves_to_surface); + WM_operatortype_append(CURVES_OT_set_selection_domain); + WM_operatortype_append(SCULPT_CURVES_OT_select_all); + WM_operatortype_append(CURVES_OT_disable_selection); } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 9e7e00d5656..59370b53995 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -777,12 +777,15 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.curves.sculpt_cut ops.curves.sculpt_delete ops.curves.sculpt_grow_shrink + ops.curves.sculpt_pinch + ops.curves.sculpt_puff ops.curves.sculpt_snake_hook ops.generic.cursor ops.generic.select ops.generic.select_box ops.generic.select_circle ops.generic.select_lasso + ops.generic.select_paint ops.gpencil.draw ops.gpencil.draw.eraser ops.gpencil.draw.line diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 17784a50bca..814c2639169 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -386,24 +386,16 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) ot->srna, "name", "Color", MAX_NAME, "Name", "Name of new color attribute"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, - {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - - static EnumPropertyItem types[3] = {{CD_PROP_COLOR, "COLOR", 0, "Color", ""}, - {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, - {0, nullptr, 0, nullptr, nullptr}}; - prop = RNA_def_enum(ot->srna, "domain", - domains, + rna_enum_color_attribute_domain_items, ATTR_DOMAIN_POINT, "Domain", "Type of element that attribute is stored on"); prop = RNA_def_enum(ot->srna, "data_type", - types, + rna_enum_color_attribute_type_items, CD_PROP_COLOR, "Data Type", "Type of data stored in attribute"); @@ -411,7 +403,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; prop = RNA_def_float_color( - ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); } diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index c6303c197e7..178a2b52b62 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -95,7 +95,7 @@ void wm_gizmo_vec_draw( immEnd(); } else if (primitive_type == GPU_PRIM_TRI_FAN) { - /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small + /* NOTE(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small * primitive counts. */ int tri_count = vert_count - 2; immBegin(GPU_PRIM_TRIS, tri_count * 3); diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index b326d6d1859..a0e30c7518a 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -74,7 +74,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz, GPU_viewport_size_get_f(viewport); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ + /* NOTE(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); /* TODO: other draw styles. */ diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index a76242404ba..1d9fc35eda8 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -96,7 +96,7 @@ static void dial_geom_draw(const float color[4], ED_GIZMO_DIAL_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); if (clip_plane) { 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 5fb1173521a..46fede8f6be 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -98,7 +98,7 @@ static void move_geom_draw(const wmGizmo *gz, ED_GIZMO_MOVE_DRAW_FLAG_FILL))); GPUVertFormat *format = immVertexFormat(); - /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR : diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 8630b7f23d4..0039dbae674 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -1482,8 +1482,8 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) * Changes here will likely apply there too. */ static const EnumPropertyItem gpencil_interpolation_type_items[] = { - /* interpolation */ - {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, + /* Interpolation. */ + RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"), {GP_IPO_LINEAR, "LINEAR", ICON_IPO_LINEAR, @@ -1495,13 +1495,10 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) "Custom", "Custom interpolation defined using a curve map"}, - /* easing */ - {0, - "", - 0, - N_("Easing (by strength)"), - "Predefined inertial transitions, useful for motion graphics (from least to most " - "''dramatic'')"}, + /* Easing. */ + RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"), + "Predefined inertial transitions, useful for motion graphics " + "(from least to most \"dramatic\")"), {GP_IPO_SINE, "SINE", ICON_IPO_SINE, @@ -1518,7 +1515,7 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) "Circular", "Circular easing (strongest and most dynamic)"}, - {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"), {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"}, {GP_IPO_BOUNCE, "BOUNCE", diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 3cdf364e4b2..01ac02a9a1d 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -146,6 +146,10 @@ typedef struct tGP_BrushEditData { float inv_mat[4][4]; RNG *rng; + /* Auto-masking strokes. */ + struct GHash *automasking_strokes; + bool automasking_ready; + } tGP_BrushEditData; /* Callback for performing some brush operation on a single point */ @@ -1182,9 +1186,18 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->region = CTX_wm_region(C); Paint *paint = &ts->gp_sculptpaint->paint; - gso->brush = paint->brush; + Brush *brush = paint->brush; + gso->brush = brush; BKE_curvemapping_init(gso->brush->curve); + if (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) { + gso->automasking_strokes = BLI_ghash_ptr_new(__func__); + } + else { + gso->automasking_strokes = NULL; + } /* save mask */ gso->mask = ts->gpencil_selectmode_sculpt; @@ -1285,6 +1298,10 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op) BLI_rng_free(gso->rng); } + if (gso->automasking_strokes != NULL) { + BLI_ghash_free(gso->automasking_strokes, NULL, NULL); + } + /* Disable headerprints. */ ED_workspace_status_text(C, NULL); @@ -1570,11 +1587,15 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, bool redo_geom = false; Object *ob = gso->object; bGPdata *gpd = ob->data; - char tool = gso->brush->gpencil_sculpt_tool; + const char tool = gso->brush->gpencil_sculpt_tool; GP_SpaceConversion *gsc = &gso->gsc; Brush *brush = gso->brush; const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : gso->brush->size; + const bool is_automasking = (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL)) != 0; /* Calc bound box matrix. */ float bound_mat[4][4]; BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); @@ -1589,6 +1610,13 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, continue; } + { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if ((is_automasking) && (!BLI_ghash_haskey(gso->automasking_strokes, gps_active))) { + continue; + } + } + /* Check if the stroke collide with brush. */ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { continue; @@ -1699,6 +1727,132 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, return changed; } +/* Get list of Auto-Masking strokes. */ +static bool get_automasking_strokes_list(tGP_BrushEditData *gso) +{ + bGPdata *gpd = gso->gpd; + GP_SpaceConversion *gsc = &gso->gsc; + Brush *brush = gso->brush; + Object *ob = gso->object; + Material *mat_active = BKE_gpencil_material(ob, ob->actcol); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_masking_stroke = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE) != 0; + const bool is_masking_layer = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER) != 0; + const bool is_masking_material = (brush->gpencil_settings->sculpt_mode_flag & + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL) != 0; + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + + /* Define a fix number of pixel as cursor radius. */ + const int radius = 10; + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Only editable and visible layers are considered. */ + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* Calculate bound box matrix. */ + float bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); + + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->totpoints == 0) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) { + continue; + } + + /* Layer Auto-Masking. */ + if ((is_masking_layer) && (gpl == gpl_active)) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + /* Material Auto-Masking. */ + if (is_masking_material) { + Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1); + if (mat == mat_active) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + continue; + } + } + + /* If Stroke Auto-Masking is not enabled, nothing else to do. */ + if (!is_masking_stroke) { + continue; + } + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { + continue; + } + + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + bGPDspoint npt; + + if (gps->totpoints == 1) { + gpencil_point_to_parent_space(gps->points, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + /* Only check if point is inside. */ + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke. + */ + for (int i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with. */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Check first point. */ + gpencil_point_to_parent_space(pt1, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + if (len_v2v2_int(mval_i, pc1) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check second point. */ + gpencil_point_to_parent_space(pt2, bound_mat, &npt); + gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + if (len_v2v2_int(mval_i, pc2) <= radius) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + + /* Check segment. */ + if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + BLI_ghash_insert(gso->automasking_strokes, gps, gps); + i = gps->totpoints; + continue; + } + } + } + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } + } + } + + return true; +} + /* Perform two-pass brushes which modify the existing strokes */ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { @@ -1840,6 +1994,14 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA * gso->brush_rect.xmax = mouse[0] + radius; gso->brush_rect.ymax = mouse[1] + radius; + /* Get list of Auto-Masking strokes. */ + if ((!gso->automasking_ready) && + (brush->gpencil_settings->sculpt_mode_flag & + (GP_SCULPT_FLAGMODE_AUTOMASK_STROKE | GP_SCULPT_FLAGMODE_AUTOMASK_LAYER | + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL))) { + gso->automasking_ready = get_automasking_strokes_list(gso); + } + /* Apply brush */ char tool = gso->brush->gpencil_sculpt_tool; if (tool == GPSCULPT_TOOL_CLONE) { diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 2dc12125f40..23c385c1213 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -3077,6 +3077,11 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, continue; } + /* Check that stroke is not closed. Closed strokes must not be included in the merge. */ + if (gps_target->flag & GP_STROKE_CYCLIC) { + continue; + } + /* Check if one of the ends is inside target stroke bounding box. */ if ((!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat)) && (!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat))) { diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 60af05baed7..da40eef87fd 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -549,7 +549,7 @@ typedef enum eAnimChannels_SetFlag { /* types of settings for AnimChannels */ typedef enum eAnimChannel_Settings { ACHANNEL_SETTING_SELECT = 0, - /** warning: for drawing UI's, need to check if this is off (maybe inverse this later) */ + /** WARNING: for drawing UI's, need to check if this is off (maybe inverse this later). */ ACHANNEL_SETTING_PROTECT = 1, ACHANNEL_SETTING_MUTE = 2, ACHANNEL_SETTING_EXPAND = 3, diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 6a730225da9..8c0147612fb 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -351,6 +351,19 @@ int ANIM_scene_get_keyingset_index(struct Scene *scene, struct KeyingSet *ks); struct KeyingSet *ANIM_get_keyingset_for_autokeying(const struct Scene *scene, const char *transformKSName); +void ANIM_keyingset_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + +void ANIM_keyingset_visit_for_search_no_poll(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); /** * Dynamically populate an enum of Keying Sets. */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 59a24ed22b6..30a98129ee6 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -384,7 +384,7 @@ void ED_operatormacros_mesh(void); */ void ED_keymap_mesh(struct wmKeyConfig *keyconf); -/* editface.c */ +/* editface.cc */ /** * Copy the face flags, most importantly selection from the mesh to the final derived mesh, @@ -520,7 +520,7 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vert */ void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr); -/* mesh_data.c */ +/* mesh_data.cc */ void ED_mesh_verts_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); @@ -536,12 +536,12 @@ void ED_mesh_geometry_clear(struct Mesh *mesh); void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose); -void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name); -int ED_mesh_uv_texture_add( +void ED_mesh_uv_ensure(struct Mesh *me, const char *name); +int ED_mesh_uv_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_uv_texture_remove_index(struct Mesh *me, int n); -bool ED_mesh_uv_texture_remove_active(struct Mesh *me); -bool ED_mesh_uv_texture_remove_named(struct Mesh *me, const char *name); +bool ED_mesh_uv_remove_index(struct Mesh *me, int n); +bool ED_mesh_uv_remove_active(struct Mesh *me); +bool ED_mesh_uv_remove_named(struct Mesh *me, const char *name); void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me); /** * Without a #bContext, called when UV-editing. @@ -591,7 +591,7 @@ void EDBM_redo_state_restore_and_free(struct BMBackup *backup, bool recalc_looptri) ATTR_NONNULL(1, 2); void EDBM_redo_state_free(struct BMBackup *backup) ATTR_NONNULL(1); -/* *** meshtools.c *** */ +/* *** meshtools.cc *** */ int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op); int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 0078c1087a0..39c7ad3556c 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -536,12 +536,12 @@ bool ED_object_modifier_move_to_index(struct ReportList *reports, struct ModifierData *md, int index); -bool ED_object_modifier_convert(struct ReportList *reports, - struct Main *bmain, - struct Depsgraph *depsgraph, - struct ViewLayer *view_layer, - struct Object *ob, - struct ModifierData *md); +bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports, + struct Main *bmain, + struct Depsgraph *depsgraph, + struct ViewLayer *view_layer, + struct Object *ob, + struct ModifierData *md); bool ED_object_modifier_apply(struct Main *bmain, struct ReportList *reports, struct Depsgraph *depsgraph, diff --git a/source/blender/editors/include/ED_scene.h b/source/blender/editors/include/ED_scene.h index f1a2e5795ee..f67bdb9c1d7 100644 --- a/source/blender/editors/include/ED_scene.h +++ b/source/blender/editors/include/ED_scene.h @@ -18,6 +18,11 @@ struct Scene *ED_scene_add(struct Main *bmain, struct bContext *C, struct wmWindow *win, enum eSceneCopyMethod method) ATTR_NONNULL(); +/** Special mode for adding a scene assigned to sequencer strip. */ +struct Scene *ED_scene_sequencer_add(struct Main *bmain, + struct bContext *C, + enum eSceneCopyMethod method, + const bool assign_strip); /** * \note Only call outside of area/region loops. * \return true if successful. diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 41123aff79f..aa62a6209e4 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -596,7 +596,7 @@ bool ED_operator_posemode_context(struct bContext *C); bool ED_operator_posemode(struct bContext *C); bool ED_operator_posemode_local(struct bContext *C); bool ED_operator_mask(struct bContext *C); -bool ED_operator_camera(struct bContext *C); +bool ED_operator_camera_poll(struct bContext *C); /* screen_user_menu.c */ @@ -666,7 +666,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2]); /** - * \note: This may return true for multiple overlapping regions. + * \note This may return true for multiple overlapping regions. * If it matters, check overlapped regions first (#ARegion.overlap). */ bool ED_region_contains_xy(const struct ARegion *region, const int event_xy[2]); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 06134f1d7b5..80a75da27f8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -24,6 +24,7 @@ struct Object; struct Scene; struct SpaceImage; struct ToolSettings; +struct View2D; struct ViewLayer; struct bContext; struct bNode; @@ -242,15 +243,12 @@ void uvedit_deselect_flush(const struct Scene *scene, struct BMEditMesh *em); */ void uvedit_select_flush(const struct Scene *scene, struct BMEditMesh *em); -bool ED_uvedit_nearest_uv(const struct Scene *scene, - struct Object *obedit, - const float co[2], - float *dist_sq, - float r_uv[2]); -bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, +bool ED_uvedit_nearest_uv_multi(const struct View2D *v2d, + const struct Scene *scene, struct Object **objects, uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 51fd8e6c533..8695e03a57f 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -7,6 +7,8 @@ #pragma once +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -256,6 +258,7 @@ typedef enum { */ V3D_PROJ_TEST_CLIP_CONTENT = (1 << 5), } eV3DProjTest; +ENUM_OPERATORS(eV3DProjTest, V3D_PROJ_TEST_CLIP_CONTENT); #define V3D_PROJ_TEST_CLIP_DEFAULT \ (V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) @@ -443,14 +446,14 @@ void pose_foreachScreenBone(struct ViewContext *vc, void ED_view3d_project_float_v2_m4(const struct ARegion *region, const float co[3], float r_co[2], - float mat[4][4]); + const float mat[4][4]); /** * \note use #ED_view3d_ob_project_mat_get to get projecting mat */ void ED_view3d_project_float_v3_m4(const struct ARegion *region, const float co[3], float r_co[3], - float mat[4][4]); + const float mat[4][4]); eV3DProjStatus ED_view3d_project_base(const struct ARegion *region, struct Base *base); @@ -700,9 +703,9 @@ void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], * \param do_clip_planes: Optionally clip the ray by the view clipping planes. * \return success, false if the segment is totally clipped. */ -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const struct ARegion *region, - struct View3D *v3d, + const struct View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], @@ -733,7 +736,7 @@ void ED_view3d_dist_range_get(const struct View3D *v3d, float r_dist_range[2]); /** * \note copies logic of #ED_view3d_viewplane_get(), keep in sync. */ -bool ED_view3d_clip_range_get(struct Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const struct Depsgraph *depsgraph, const struct View3D *v3d, const struct RegionView3D *rv3d, float *r_clipsta, @@ -952,6 +955,22 @@ float ED_view3d_select_dist_px(void); void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc, struct Depsgraph *depsgraph); + +/** + * Re-initialize `vc` with `obact` as if it's active object (with some differences). + * + * This is often used when operating on multiple objects in modes (edit, pose mode etc) + * where the `vc` is passed in as an argument which then references it's object data. + * + * \note members #ViewContext.obedit & #ViewContext.em are only initialized if they're already set, + * by #ED_view3d_viewcontext_init in most cases. + * This is necessary because the active object defines the current object-mode. + * When iterating over objects in object-mode it doesn't make sense to perform + * an edit-mode action on an object that happens to contain edit-mode data. + * In some cases these values are cleared allowing the owner of `vc` to explicitly + * disable edit-mode operation (to force object selection in edit-mode for e.g.). + * So object-mode specific values should remain cleared when initialized with another object. + */ void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); /** * Use this call when executing an operator, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d1a6501408c..ea1095b26ff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON_BLANK(181) +DEF_ICON(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1b61e87b140..a9a9c98cab8 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -202,7 +202,7 @@ enum { UI_BUT_INACTIVE = 1 << 18, UI_BUT_LAST_ACTIVE = 1 << 19, UI_BUT_UNDO = 1 << 20, - UI_BUT_IMMEDIATE = 1 << 21, + /* UNUSED = 1 << 21, */ UI_BUT_NO_UTF8 = 1 << 22, /** For popups, pressing return activates this button, overriding the highlighted button. @@ -221,7 +221,7 @@ enum { UI_BUT_HAS_SEP_CHAR = 1 << 27, /** Don't run updates while dragging (needed in rare cases). */ UI_BUT_UPDATE_DELAY = 1 << 28, - /** When widget is in textedit mode, update value on each char stroke */ + /** When widget is in text-edit mode, update value on each char stroke. */ UI_BUT_TEXTEDIT_UPDATE = 1 << 29, /** Show 'x' icon to clear/unlink value of text or search button. */ UI_BUT_VALUE_CLEAR = 1 << 30, @@ -454,7 +454,6 @@ void UI_draw_safe_areas(uint pos, enum { UI_SCROLL_PRESSED = 1 << 0, UI_SCROLL_ARROWS = 1 << 1, - UI_SCROLL_NO_OUTLINE = 1 << 2, }; /** * Function in use for buttons and for view2d sliders. @@ -1539,31 +1538,6 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, short height, const char *tip); -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip); - -/** - * Short pointers hard-coded. - * \param modkeypoin: will be set to #KM_SHIFT, #KM_ALT, #KM_CTRL, #KM_OSKEY bits. - */ -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip); - /** * \param arg: A pointer to string/name, use #UI_but_func_search_set() below to make this work. * here `a1` and `a2`, if set, control thumbnail preview rows/cols. @@ -1674,15 +1648,16 @@ void UI_but_func_identity_compare_set(uiBut *but, uiButIdentityCompareFunc cmp_f * \param name: Text to display for the item. * \param poin: Opaque pointer (for use by the caller). * \param iconid: The icon, #ICON_NONE for no icon. - * \param state: The buttons state flag, compatible with #uiBut.flag, - * typically #UI_BUT_DISABLED / #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_BUT_DISABLED, #UI_BUT_INACTIVE or #UI_BUT_HAS_SEP_CHAR. + * * \return false if there is nothing to add. */ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + int but_flag, uint8_t name_prefix_offset); /** diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 6416421f4f5..a1a98a4b08c 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -92,7 +92,7 @@ void UI_icon_render_id_ex(const struct bContext *C, int UI_icon_preview_to_render_size(enum eIconSizes size); /** - * Draws icon with dpi scale factor. + * Draws icon with DPI scale factor. */ void UI_icon_draw(float x, float y, int icon_id); void UI_icon_draw_alpha(float x, float y, int icon_id, float alpha); diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 76dead57b0a..c12d6957a95 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -3852,21 +3852,22 @@ static void ui_but_update_ex(uiBut *but, const bool validate) } case UI_BTYPE_HOTKEY_EVENT: if (but->flag & UI_SELECT) { + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; - if (but->modifier_key) { + if (hotkey_but->modifier_key) { char *str = but->drawstr; but->drawstr[0] = '\0'; - if (but->modifier_key & KM_SHIFT) { + if (hotkey_but->modifier_key & KM_SHIFT) { str += BLI_strcpy_rlen(str, "Shift "); } - if (but->modifier_key & KM_CTRL) { + if (hotkey_but->modifier_key & KM_CTRL) { str += BLI_strcpy_rlen(str, "Ctrl "); } - if (but->modifier_key & KM_ALT) { + if (hotkey_but->modifier_key & KM_ALT) { str += BLI_strcpy_rlen(str, "Alt "); } - if (but->modifier_key & KM_OSKEY) { + if (hotkey_but->modifier_key & KM_OSKEY) { str += BLI_strcpy_rlen(str, "Cmd "); } @@ -3992,6 +3993,10 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButTreeRow); alloc_str = "uiButTreeRow"; break; + case UI_BTYPE_HOTKEY_EVENT: + alloc_size = sizeof(uiButHotkeyEvent); + alloc_str = "uiButHotkeyEvent"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -6283,64 +6288,6 @@ uiBut *uiDefIconBlockBut(uiBlock *block, return but; } -uiBut *uiDefKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *spoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_KEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - spoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - ui_but_update(but); - return but; -} - -uiBut *uiDefHotKeyevtButS(uiBlock *block, - int retval, - const char *str, - int x, - int y, - short width, - short height, - short *keypoin, - const short *modkeypoin, - const char *tip) -{ - uiBut *but = ui_def_but(block, - UI_BTYPE_HOTKEY_EVENT | UI_BUT_POIN_SHORT, - retval, - str, - x, - y, - width, - height, - keypoin, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - but->modifier_key = *modkeypoin; - ui_but_update(but); - return but; -} - uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 3e3b30a2c1e..a7dfff2edb4 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -542,7 +542,7 @@ static bool but_copypaste_profile_alive = false; bool ui_but_is_editing(const uiBut *but) { - uiHandleButtonData *data = but->active; + const uiHandleButtonData *data = but->active; return (data && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING)); } @@ -660,21 +660,23 @@ static bool ui_but_dragedit_update_mval(uiHandleButtonData *data, int mx) static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop) { /* Not very elegant, but ensures preference changes force re-save. */ - bool tag = false; - if (prop && !(RNA_property_flag(prop) & PROP_NO_DEG_UPDATE)) { - StructRNA *base = RNA_struct_base(ptr->type); - if (base == NULL) { - base = ptr->type; - } - if (ELEM(base, - &RNA_AddonPreferences, - &RNA_KeyConfigPreferences, - &RNA_KeyMapItem, - &RNA_UserAssetLibrary)) { - tag = true; - } + + if (!prop) { + return false; } - return tag; + if (RNA_property_flag(prop) & PROP_NO_DEG_UPDATE) { + return false; + } + + StructRNA *base = RNA_struct_base(ptr->type); + if (base == NULL) { + base = ptr->type; + } + return ELEM(base, + &RNA_AddonPreferences, + &RNA_KeyConfigPreferences, + &RNA_KeyMapItem, + &RNA_UserAssetLibrary); } bool UI_but_is_userdef(const uiBut *but) @@ -900,64 +902,66 @@ static void ui_apply_but_func(bContext *C, uiBut *but) /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ static void ui_apply_but_undo(uiBut *but) { - if (but->flag & UI_BUT_UNDO) { - const char *str = NULL; - size_t str_len_clip = SIZE_MAX - 1; - bool skip_undo = false; + if (!(but->flag & UI_BUT_UNDO)) { + return; + } - /* define which string to use for undo */ - if (but->type == UI_BTYPE_MENU) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else if (but->drawstr[0]) { - str = but->drawstr; - str_len_clip = ui_but_drawstr_len_without_sep_char(but); - } - else { - str = but->tip; - str_len_clip = ui_but_tip_len_only_first_line(but); - } + const char *str = NULL; + size_t str_len_clip = SIZE_MAX - 1; + bool skip_undo = false; - /* fallback, else we don't get an undo! */ - if (str == NULL || str[0] == '\0' || str_len_clip == 0) { - str = "Unknown Action"; - str_len_clip = strlen(str); - } + /* define which string to use for undo */ + if (but->type == UI_BTYPE_MENU) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else if (but->drawstr[0]) { + str = but->drawstr; + str_len_clip = ui_but_drawstr_len_without_sep_char(but); + } + else { + str = but->tip; + str_len_clip = ui_but_tip_len_only_first_line(but); + } - /* Optionally override undo when undo system doesn't support storing properties. */ - if (but->rnapoin.owner_id) { - /* Exception for renaming ID data, we always need undo pushes in this case, - * because undo systems track data by their ID, see: T67002. */ - /* Exception for active shape-key, since changing this in edit-mode updates - * the shape key from object mode data. */ - if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { - /* pass */ - } - else { - ID *id = but->rnapoin.owner_id; - if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { - skip_undo = true; - } - } - } + /* fallback, else we don't get an undo! */ + if (str == NULL || str[0] == '\0' || str_len_clip == 0) { + str = "Unknown Action"; + str_len_clip = strlen(str); + } - if (skip_undo == false) { - /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo - * steps to be written which cause lag: T71434. */ - if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + /* Optionally override undo when undo system doesn't support storing properties. */ + if (but->rnapoin.owner_id) { + /* Exception for renaming ID data, we always need undo pushes in this case, + * because undo systems track data by their ID, see: T67002. */ + /* Exception for active shape-key, since changing this in edit-mode updates + * the shape key from object mode data. */ + if (ELEM(but->rnaprop, &rna_ID_name, &rna_Object_active_shape_key_index)) { + /* pass */ + } + else { + ID *id = but->rnapoin.owner_id; + if (!ED_undo_is_legacy_compatible_for_property(but->block->evil_C, id)) { skip_undo = true; } } + } - if (skip_undo) { - str = ""; + if (skip_undo == false) { + /* XXX: disable all undo pushes from UI changes from sculpt mode as they cause memfile undo + * steps to be written which cause lag: T71434. */ + if (BKE_paintmode_get_active_from_context(but->block->evil_C) == PAINT_MODE_SCULPT) { + skip_undo = true; } + } - /* delayed, after all other funcs run, popups are closed, etc */ - uiAfterFunc *after = ui_afterfunc_new(); - BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); + if (skip_undo) { + str = ""; } + + /* delayed, after all other funcs run, popups are closed, etc */ + uiAfterFunc *after = ui_afterfunc_new(); + BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr))); } static void ui_apply_but_autokey(bContext *C, uiBut *but) @@ -967,21 +971,21 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but) /* try autokey */ ui_but_anim_autokey(C, but, scene, scene->r.cfra); - /* make a little report about what we've done! */ - if (but->rnaprop) { - char *buf; + if (!but->rnaprop) { + return; + } - if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { - return; - } + if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { + return; + } - buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); - if (buf) { - BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); - MEM_freeN(buf); + /* make a little report about what we've done! */ + char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); + if (buf) { + BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); + MEM_freeN(buf); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); - } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL); } } @@ -1631,29 +1635,34 @@ static bool ui_drag_toggle_set_xy_xy( LISTBASE_FOREACH (uiBut *, but, &block->buttons) { /* NOTE: ctrl is always true here because (at least for now) * we always want to consider text control in this case, even when not embossed. */ - if (ui_but_is_interactive(but, true)) { - if (BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { - - /* execute the button */ - if (ui_drag_toggle_but_is_supported(but)) { - /* is it pressed? */ - const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); - if (pushed_state_but != pushed_state) { - UI_but_execute(C, region, but); - if (do_check) { - ui_but_update_edited(but); - } - if (U.runtime.is_dirty == false) { - ui_but_update_preferences_dirty(but); - } - changed = true; - } - } - /* done */ - } + + if (!ui_but_is_interactive(but, true)) { + continue; + } + if (!BLI_rctf_isect_segment(&but->rect, xy_a_block, xy_b_block)) { + continue; + } + if (!ui_drag_toggle_but_is_supported(but)) { + continue; + } + /* is it pressed? */ + const int pushed_state_but = ui_drag_toggle_but_pushed_state(but); + if (pushed_state_but == pushed_state) { + continue; + } + + /* execute the button */ + UI_but_execute(C, region, but); + if (do_check) { + ui_but_update_edited(but); } + if (U.runtime.is_dirty == false) { + ui_but_update_preferences_dirty(but); + } + changed = true; } } + if (changed) { /* apply now, not on release (or if handlers are canceled for whatever reason) */ ui_apply_but_funcs_after(C); @@ -4507,10 +4516,14 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiHandleButtonData *data, const wmEvent *event) { + uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY, EVT_BUT_OPEN) && + (event->val == KM_PRESS)) { but->drawstr[0] = 0; - but->modifier_key = 0; + hotkey_but->modifier_key = 0; button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); return WM_UI_HANDLER_BREAK; } @@ -4531,20 +4544,16 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) { - /* data->cancel doesn't work, this button opens immediate */ - if (but->flag & UI_BUT_IMMEDIATE) { - ui_but_value_set(but, 0); - } - else { - data->cancel = true; - } + data->cancel = true; + /* Close the containing popup (if any). */ + data->escapecancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } } /* always set */ - but->modifier_key = event->modifier; + hotkey_but->modifier_key = event->modifier; ui_but_update(but); ED_region_tag_redraw(data->region); @@ -8498,14 +8507,6 @@ static void button_activate_init(bContext *C, } button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); - /* activate right away */ - if (but->flag & UI_BUT_IMMEDIATE) { - if (but->type == UI_BTYPE_HOTKEY_EVENT) { - button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); - } - /* .. more to be added here */ - } - if (type == BUTTON_ACTIVATE_OPEN) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 1c79d3218fb..1b245ba9e6f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -229,7 +229,6 @@ struct uiBut { bool changed; /** so buttons can support unit systems which are not RNA */ uchar unit_type; - short modifier_key; short iconadd; /** #UI_BTYPE_BLOCK data */ @@ -381,6 +380,13 @@ typedef struct uiButCurveMapping { eButGradientType gradient_type; } uiButCurveMapping; +/** Derived struct for #UI_BTYPE_HOTKEY_EVENT. */ +typedef struct uiButHotkeyEvent { + uiBut but; + + short modifier_key; +} uiButHotkeyEvent; + /** * Additional, superimposed icon for a button, invoking an operator. */ @@ -1212,24 +1218,24 @@ typedef enum { /** * Helper call to draw a menu item without a button. * - * \param state: The state of the button, - * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. + * \param but_flag: Button flags (#uiBut.flag) indicating the state of the item, typically + * #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. * \param separator_type: The kind of separator which controls if and how the string is clipped. - * \param r_xmax: The right hand position of the text, this takes into the icon, - * padding and text clipping when there is not enough room to display the full text. + * \param r_xmax: The right hand position of the text, this takes into the icon, padding and text + * clipping when there is not enough room to display the full text. */ void ui_draw_menu_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax); void ui_draw_preview_item(const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align); /** * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index c1bb2ed6d18..3465373c85d 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -961,11 +961,17 @@ static void ui_item_enum_expand_tabs(uiLayout *layout, static void ui_keymap_but_cb(bContext *UNUSED(C), void *but_v, void *UNUSED(key_v)) { uiBut *but = but_v; + BLI_assert(but->type == UI_BTYPE_HOTKEY_EVENT); + const uiButHotkeyEvent *hotkey_but = (uiButHotkeyEvent *)but; - RNA_int_set(&but->rnapoin, "shift", (but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "ctrl", (but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "alt", (but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); - RNA_int_set(&but->rnapoin, "oskey", (but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "shift", (hotkey_but->modifier_key & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "ctrl", (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING); + RNA_int_set( + &but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING); } /** @@ -1101,7 +1107,7 @@ static uiBut *ui_item_with_label(uiLayout *layout, NULL); UI_but_func_set(but, ui_keymap_but_cb, but, NULL); if (flag & UI_ITEM_R_IMMEDIATE) { - UI_but_flag_enable(but, UI_BUT_IMMEDIATE); + UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); } } else { @@ -2332,7 +2338,14 @@ void uiItemFullR(uiLayout *layout, /* property with separate label */ else if (ELEM(type, PROP_ENUM, PROP_STRING, PROP_POINTER)) { but = ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h, flag); - but = ui_but_add_search(but, ptr, prop, NULL, NULL, false); + bool results_are_suggestions = false; + if (type == PROP_STRING) { + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag(prop); + if (search_flag & PROP_STRING_SEARCH_SUGGESTION) { + results_are_suggestions = true; + } + } + but = ui_but_add_search(but, ptr, prop, NULL, NULL, results_are_suggestions); if (layout->redalert) { UI_but_flag_enable(but, UI_BUT_REDALERT); @@ -2705,11 +2718,16 @@ uiBut *ui_but_add_search(uiBut *but, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, - bool results_are_suggestions) + const bool results_are_suggestions) { /* for ID's we do automatic lookup */ + bool has_search_fn = false; + PointerRNA sptr; if (!searchprop) { + if (RNA_property_type(prop) == PROP_STRING) { + has_search_fn = (RNA_property_string_search_flag(prop) != 0); + } if (RNA_property_type(prop) == PROP_POINTER) { StructRNA *ptype = RNA_property_pointer_type(ptr, prop); search_id_collection(ptype, &sptr, &searchprop); @@ -2718,14 +2736,18 @@ uiBut *ui_but_add_search(uiBut *but, } /* turn button into search button */ - if (searchprop) { + if (has_search_fn || searchprop) { uiRNACollectionSearch *coll_search = MEM_mallocN(sizeof(*coll_search), __func__); uiButSearch *search_but; but = ui_but_change_type(but, UI_BTYPE_SEARCH_MENU); search_but = (uiButSearch *)but; - search_but->rnasearchpoin = *searchptr; - search_but->rnasearchprop = searchprop; + + if (searchptr) { + search_but->rnasearchpoin = *searchptr; + search_but->rnasearchprop = searchprop; + } + but->hardmax = MAX2(but->hardmax, 256.0f); but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT; if (RNA_property_is_unlink(prop)) { @@ -2734,8 +2756,17 @@ uiBut *ui_but_add_search(uiBut *but, coll_search->target_ptr = *ptr; coll_search->target_prop = prop; - coll_search->search_ptr = *searchptr; - coll_search->search_prop = searchprop; + + if (searchptr) { + coll_search->search_ptr = *searchptr; + coll_search->search_prop = searchprop; + } + else { + /* Rely on `has_search_fn`. */ + coll_search->search_ptr = PointerRNA_NULL; + coll_search->search_prop = NULL; + } + coll_search->search_but = but; coll_search->butstore_block = but->block; coll_search->butstore = UI_butstore_create(coll_search->butstore_block); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index f14c6ad9924..c066ced21cb 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -2142,11 +2142,8 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - if (!RNA_struct_property_is_set(op->ptr, "session_uuid")) { - return OPERATOR_CANCELLED; - } - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - Material *ma = (Material *)BKE_libblock_find_session_uuid(bmain, ID_MA, session_uuid); + Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_MA); if (ma == NULL) { return OPERATOR_CANCELLED; } @@ -2184,16 +2181,7 @@ static void UI_OT_drop_material(wmOperatorType *ot) ot->exec = ui_drop_material_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - PropertyRNA *prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + WM_operator_properties_id_lookup(ot, false); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index bc497e2647c..64de31dfe6a 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -58,7 +58,7 @@ struct uiSearchItems { char **names; void **pointers; int *icons; - int *states; + int *but_flags; uint8_t *name_prefix_offsets; /** Is there any item with an icon? */ @@ -94,7 +94,7 @@ bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, - int state, + const int but_flag, const uint8_t name_prefix_offset) { /* hijack for autocomplete */ @@ -148,10 +148,10 @@ bool UI_search_item_add(uiSearchItems *items, /* Limit flags that can be set so flags such as 'UI_SELECT' aren't accidentally set * which will cause problems, add others as needed. */ - BLI_assert( - (state & ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); - if (items->states) { - items->states[items->totitem] = state; + BLI_assert((but_flag & + ~(UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_BUT_REDALERT | UI_BUT_HAS_SEP_CHAR)) == 0); + if (items->but_flags) { + items->but_flags[items->totitem] = but_flag; } items->totitem++; @@ -556,7 +556,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) if (data->preview) { /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; /* ensure icon is up-to-date */ ui_icon_ensure_deferred(C, data->items.icons[a], data->preview); @@ -568,7 +568,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, data->items.names[a], data->items.icons[a], - state, + but_flag, UI_STYLE_TEXT_LEFT); } @@ -590,7 +590,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0; /* draw items */ for (int a = 0; a < data->items.totitem; a++) { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; char *name = data->items.names[a]; int icon = data->items.icons[a]; char *name_sep_test = nullptr; @@ -600,7 +600,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) separator_type = UI_MENU_ITEM_SEPARATOR_SHORTCUT; } /* Only set for displaying additional hint (e.g. library name of a linked data-block). */ - else if (state & UI_BUT_HAS_SEP_CHAR) { + else if (but_flag & UI_BUT_HAS_SEP_CHAR) { separator_type = UI_MENU_ITEM_SEPARATOR_HINT; } @@ -615,7 +615,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* Simple menu item. */ - ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, separator_type, nullptr); + ui_draw_menu_item(&data->fstyle, &rect, name, icon, but_flag, separator_type, nullptr); } else { /* Split menu item, faded text before the separator. */ @@ -633,7 +633,7 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) &rect, name, 0, - state | UI_BUT_INACTIVE, + but_flag | UI_BUT_INACTIVE, UI_MENU_ITEM_SEPARATOR_NONE, &name_width); *name_sep = name_sep_prev; @@ -646,7 +646,8 @@ static void ui_searchbox_region_draw_fn(const bContext *C, ARegion *region) } /* The previous menu item draws the active selection. */ - ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, nullptr); + ui_draw_menu_item( + &data->fstyle, &rect, name_sep, icon, but_flag, separator_type, nullptr); } } /* indicate more */ @@ -677,7 +678,7 @@ static void ui_searchbox_region_free_fn(ARegion *region) MEM_freeN(data->items.names); MEM_freeN(data->items.pointers); MEM_freeN(data->items.icons); - MEM_freeN(data->items.states); + MEM_freeN(data->items.but_flags); if (data->items.name_prefix_offsets != nullptr) { MEM_freeN(data->items.name_prefix_offsets); @@ -847,7 +848,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.names = (char **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.pointers = (void **)MEM_callocN(data->items.maxitem * sizeof(void *), __func__); data->items.icons = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); - data->items.states = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); + data->items.but_flags = (int *)MEM_callocN(data->items.maxitem * sizeof(int), __func__); data->items.name_prefix_offsets = nullptr; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { data->items.names[i] = (char *)MEM_callocN(data->items.maxstrlen + 1, __func__); @@ -913,7 +914,7 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe /* widget itself */ /* NOTE: i18n messages extracting tool does the same, please keep it in sync. */ { - const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + const int but_flag = ((a == data->active) ? UI_ACTIVE : 0) | data->items.but_flags[a]; wmOperatorType *ot = static_cast<wmOperatorType *>(data->items.pointers[a]); char text_pre[128]; @@ -936,14 +937,14 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe &rect_pre, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre), data->items.icons[a], - state, + but_flag, UI_MENU_ITEM_SEPARATOR_NONE, nullptr); ui_draw_menu_item(&data->fstyle, &rect_post, data->items.names[a], 0, - state, + but_flag, data->use_shortcut_sep ? UI_MENU_ITEM_SEPARATOR_SHORTCUT : UI_MENU_ITEM_SEPARATOR_NONE, nullptr); diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc index 0156a943015..291ede05730 100644 --- a/source/blender/editors/interface/interface_style.cc +++ b/source/blender/editors/interface/interface_style.cc @@ -376,7 +376,7 @@ void uiStyleInit(void) { const uiStyle *style = static_cast<uiStyle *>(U.uistyles.first); - /* recover from uninitialized dpi */ + /* Recover from uninitialized DPI. */ if (U.dpi == 0) { U.dpi = 72; } diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index dc8f568d025..65764e31ec8 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -91,6 +91,9 @@ void attribute_search_add_items(StringRefNull str, if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { continue; } + if (!bke::allow_procedural_attribute_access(item->name)) { + continue; + } BLI_string_search_add(search, item->name.c_str(), (void *)item, 0); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index c6e5deb314e..a02e8a3ac49 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -742,7 +742,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } else if (object_active != NULL && !ID_IS_LINKED(object_active) && &object_active->instance_collection->id == id) { @@ -754,7 +754,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C, id, &object_active->id, &object_active->id, - &id_override); + &id_override, + false); } break; case ID_OB: @@ -765,7 +766,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, object_active->id.tag |= LIB_TAG_DOIT; } BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); } break; case ID_ME: @@ -787,13 +788,20 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + false); } else { object_active->id.tag |= LIB_TAG_DOIT; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override); + bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false); } } break; diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc index 993ccdf92f7..b7ca2d9aa11 100644 --- a/source/blender/editors/interface/interface_utils.cc +++ b/source/blender/editors/interface/interface_utils.cc @@ -24,6 +24,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -516,71 +517,136 @@ void ui_rna_collection_search_update_fn( StringSearch *search = skip_filter ? nullptr : BLI_string_search_new(); - /* build a temporary list of relevant items first */ - int item_index = 0; - RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { + if (data->search_prop != nullptr) { + /* build a temporary list of relevant items first */ + int item_index = 0; + RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { - if (flag & PROP_ID_SELF_CHECK) { - if (itemptr.data == data->target_ptr.owner_id) { - continue; + if (flag & PROP_ID_SELF_CHECK) { + if (itemptr.data == data->target_ptr.owner_id) { + continue; + } } - } - /* use filter */ - if (is_ptr_target) { - if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { - continue; + /* use filter */ + if (is_ptr_target) { + if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) { + continue; + } } - } - int name_prefix_offset = 0; - int iconid = ICON_NONE; - bool has_sep_char = false; - const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); + int name_prefix_offset = 0; + int iconid = ICON_NONE; + bool has_sep_char = false; + const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); - if (is_id) { - iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); - if (!ELEM(iconid, 0, ICON_BLANK1)) { - has_id_icon = true; - } + if (is_id) { + iconid = ui_id_icon_get(C, static_cast<ID *>(itemptr.data), false); + if (!ELEM(iconid, 0, ICON_BLANK1)) { + has_id_icon = true; + } - if (requires_exact_data_name) { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + if (requires_exact_data_name) { + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); + } + else { + const ID *id = static_cast<ID *>(itemptr.data); + BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); + BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, + "Name string buffer should be big enough to hold full UI ID name"); + name = name_buf; + has_sep_char = ID_IS_LINKED(id); + } } else { - const ID *id = static_cast<ID *>(itemptr.data); - BKE_id_full_name_ui_prefix_get(name_buf, id, true, UI_SEP_CHAR, &name_prefix_offset); - BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, - "Name string buffer should be big enough to hold full UI ID name"); - name = name_buf; - has_sep_char = ID_IS_LINKED(id); + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); } - } - else { - name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr); - } - if (name) { - CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); - cis->data = itemptr.data; - cis->name = BLI_strdup(name); - cis->index = item_index; - cis->iconid = iconid; - cis->is_id = is_id; - cis->name_prefix_offset = name_prefix_offset; - cis->has_sep_char = has_sep_char; - if (!skip_filter) { - BLI_string_search_add(search, name, cis, 0); + if (name) { + CollItemSearch *cis = MEM_cnew<CollItemSearch>(__func__); + cis->data = itemptr.data; + cis->name = BLI_strdup(name); + cis->index = item_index; + cis->iconid = iconid; + cis->is_id = is_id; + cis->name_prefix_offset = name_prefix_offset; + cis->has_sep_char = has_sep_char; + if (!skip_filter) { + BLI_string_search_add(search, name, cis, 0); + } + BLI_addtail(items_list, cis); + if (name != name_buf) { + MEM_freeN(name); + } } - BLI_addtail(items_list, cis); - if (name != name_buf) { - MEM_freeN(name); + + item_index++; + } + RNA_PROP_END; + } + else { + BLI_assert(RNA_property_type(data->target_prop) == PROP_STRING); + const eStringPropertySearchFlag search_flag = RNA_property_string_search_flag( + data->target_prop); + BLI_assert(search_flag & PROP_STRING_SEARCH_SUPPORTED); + + struct SearchVisitUserData { + StringSearch *search; + bool skip_filter; + int item_index; + ListBase *items_list; + const char *func_id; + } user_data = {nullptr}; + + user_data.search = search; + user_data.skip_filter = skip_filter; + user_data.items_list = items_list; + user_data.func_id = __func__; + + RNA_property_string_search( + C, + &data->target_ptr, + data->target_prop, + str, + [](void *user_data, const StringPropertySearchVisitParams *visit_params) { + const bool show_extra_info = (G.debug_value == 102); + + SearchVisitUserData *search_data = (struct SearchVisitUserData *)user_data; + CollItemSearch *cis = MEM_cnew<CollItemSearch>(search_data->func_id); + cis->data = nullptr; + if (visit_params->info && show_extra_info) { + cis->name = BLI_sprintfN( + "%s" UI_SEP_CHAR_S "%s", visit_params->text, visit_params->info); + } + else { + cis->name = BLI_strdup(visit_params->text); + } + cis->index = search_data->item_index; + cis->iconid = ICON_NONE; + cis->is_id = false; + cis->name_prefix_offset = 0; + cis->has_sep_char = visit_params->info != nullptr; + if (!search_data->skip_filter) { + BLI_string_search_add(search_data->search, visit_params->text, cis, 0); + } + BLI_addtail(search_data->items_list, cis); + search_data->item_index++; + }, + (void *)&user_data); + + if (search_flag & PROP_STRING_SEARCH_SORT) { + BLI_listbase_sort(items_list, [](const void *a_, const void *b_) -> int { + const CollItemSearch *cis_a = (const CollItemSearch *)a_; + const CollItemSearch *cis_b = (const CollItemSearch *)b_; + return BLI_strcasecmp_natural(cis_a->name, cis_b->name); + }); + int i = 0; + LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { + cis->index = i; + i++; } } - - item_index++; } - RNA_PROP_END; if (skip_filter) { LISTBASE_FOREACH (CollItemSearch *, cis, items_list) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 98ecf91adbc..3777ff31b26 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -107,22 +107,22 @@ typedef enum { UI_WTYPE_TREEROW, } uiWidgetTypeEnum; -/* Button state argument shares bits with 'uiBut.flag'. - * reuse flags that aren't needed for drawing to avoid collision. */ -enum { - /* Show that holding the button opens a menu. */ - UI_STATE_HOLD_ACTION = UI_BUT_UPDATE_DELAY, - UI_STATE_TEXT_INPUT = UI_BUT_UNDO, - UI_STATE_ACTIVE_LEFT = UI_BUT_VALUE_CLEAR, - UI_STATE_ACTIVE_RIGHT = UI_BUT_TEXTEDIT_UPDATE, - UI_STATE_TEXT_BEFORE_WIDGET = UI_BUT_IMMEDIATE, - - UI_STATE_FLAGS_ALL = (UI_STATE_HOLD_ACTION | UI_STATE_TEXT_INPUT | UI_STATE_ACTIVE_LEFT | - UI_STATE_ACTIVE_RIGHT | UI_STATE_TEXT_BEFORE_WIDGET), -}; -/* Prevent accidental use. */ -#define UI_BUT_UPDATE_DELAY ((void)0) -#define UI_BUT_UNDO ((void)0) +/** + * The button's state information adapted for drawing. Use #STATE_INFO_NULL for empty state. + */ +typedef struct { + /** Copy of #uiBut.flag (possibly with overrides for drawing). */ + int but_flag; + /** Copy of #uiBut.drawflag (possibly with overrides for drawing). */ + int but_drawflag; + + /** Show that holding the button opens a menu. */ + bool has_hold_action : 1; + /** The button is in text input mode. */ + bool is_text_input : 1; +} uiWidgetStateInfo; + +static const uiWidgetStateInfo STATE_INFO_NULL = {0}; /** \} */ @@ -256,10 +256,21 @@ typedef struct uiWidgetType { /* converted colors for state */ uiWidgetColors wcol; - void (*state)(struct uiWidgetType *, int state, int drawflag, eUIEmbossType emboss); - void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); - void (*custom)( - uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign, const float zoom); + void (*state)(struct uiWidgetType *, const uiWidgetStateInfo *state, eUIEmbossType emboss) + ATTR_NONNULL(); + void (*draw)(uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*custom)(uiBut *, + uiWidgetColors *, + rcti *, + const uiWidgetStateInfo *, + int roundboxalign, + const float zoom) ATTR_NONNULL(); + void (*draw_block)( + uiWidgetColors *, rcti *, int block_flag, int roundboxalign, const float zoom); void (*text)(const uiFontStyle *, const uiWidgetColors *, uiBut *, rcti *); } uiWidgetType; @@ -1289,16 +1300,16 @@ static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) #define PREVIEW_PAD 4 -static float widget_alpha_factor(const int state) +static float widget_alpha_factor(const uiWidgetStateInfo *state) { - if (state & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & (UI_BUT_INACTIVE | UI_BUT_DISABLED)) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.25f; } return 0.5f; } - if (state & UI_SEARCH_FILTER_NO_MATCH) { + if (state->but_flag & UI_SEARCH_FILTER_NO_MATCH) { return 0.5f; } @@ -1367,7 +1378,10 @@ static void widget_draw_icon( } } else if (ELEM(but->type, UI_BTYPE_BUT, UI_BTYPE_DECORATOR)) { - alpha *= widget_alpha_factor(but->flag); + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; + alpha *= widget_alpha_factor(&state); } GPU_blend(GPU_BLEND_ALPHA); @@ -2446,7 +2460,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, * \{ */ /* put all widget colors on half alpha, use local storage */ -static void ui_widget_color_disabled(uiWidgetType *wt, const int state) +static void ui_widget_color_disabled(uiWidgetType *wt, const uiWidgetStateInfo *state) { static uiWidgetColors wcol_theme_s; @@ -2473,8 +2487,7 @@ static void widget_active_color(uiWidgetColors *wcol) } static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state, - int state, - int drawflag, + const uiWidgetStateInfo *state, const eUIEmbossType emboss) { /* Explicitly require #UI_EMBOSS_NONE_OR_STATUS for color blending with no emboss. */ @@ -2482,44 +2495,44 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco return NULL; } - if (drawflag & UI_BUT_ANIMATED_CHANGED) { + if (state->but_drawflag & UI_BUT_ANIMATED_CHANGED) { return wcol_state->inner_changed_sel; } - if (state & UI_BUT_ANIMATED_KEY) { + if (state->but_flag & UI_BUT_ANIMATED_KEY) { return wcol_state->inner_key_sel; } - if (state & UI_BUT_ANIMATED) { + if (state->but_flag & UI_BUT_ANIMATED) { return wcol_state->inner_anim_sel; } - if (state & UI_BUT_DRIVEN) { + if (state->but_flag & UI_BUT_DRIVEN) { return wcol_state->inner_driven_sel; } - if (state & UI_BUT_OVERRIDDEN) { + if (state->but_flag & UI_BUT_OVERRIDDEN) { return wcol_state->inner_overridden_sel; } return NULL; } /* copy colors from theme, and set changes in it based on state */ -static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state(uiWidgetType *wt, const uiWidgetStateInfo *state, eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default widget's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { ui_widget_color_disabled(wt, state); } } wt->wcol = *(wt->wcol_theme); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); @@ -2527,12 +2540,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } else { - if (state & UI_BUT_ACTIVE_DEFAULT) { + if (state->but_flag & UI_BUT_ACTIVE_DEFAULT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); } @@ -2544,12 +2557,12 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp * even if UI_SELECT. But currently this causes some flickering * as buttons can be created and updated without respect to mouse * position and so can draw without UI_ACTIVE set. See D6503. */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { widget_active_color(&wt->wcol); } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; if (wt->draw && emboss != UI_EMBOSS_NONE) { color_blend_v3_v3(wt->wcol.inner, red, 0.4f); @@ -2559,14 +2572,14 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp } } - if (state & UI_BUT_DRAG_MULTI) { + if (state->but_flag & UI_BUT_DRAG_MULTI) { /* the button isn't SELECT but we're editing this so draw with sel color */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f); } - if (state & UI_BUT_NODE_ACTIVE) { + if (state->but_flag & UI_BUT_NODE_ACTIVE) { const uchar blue[4] = {86, 128, 194}; color_blend_v3_v3(wt->wcol.inner, blue, 0.3f); } @@ -2600,14 +2613,16 @@ static float widget_radius_from_rcti(const rcti *rect, const uiWidgetColors *wco * \{ */ /* sliders use special hack which sets 'item' as inner when drawing filling */ -static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_numslider(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { uiWidgetStateColors *wcol_state = wt->wcol_state; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); - const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, drawflag, emboss); + const uchar *color_blend = widget_color_blend_from_flags(wcol_state, state, emboss); if (color_blend != NULL) { /* Set the slider 'item' so that it reflects state settings too. * De-saturate so the color of the slider doesn't conflict with the blend color, @@ -2617,15 +2632,14 @@ static void widget_state_numslider(uiWidgetType *wt, int state, int drawflag, eU color_ensure_contrast_v3(wt->wcol.item, wt->wcol.inner, 30); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); } } /* labels use theme colors for text */ static void widget_state_option_menu(uiWidgetType *wt, - int state, - int drawflag, + const uiWidgetStateInfo *state, eUIEmbossType emboss) { const bTheme *btheme = UI_GetTheme(); @@ -2638,14 +2652,13 @@ static void widget_state_option_menu(uiWidgetType *wt, copy_v3_v3_uchar(wcol_menu_option.text_sel, btheme->tui.wcol_menu_back.text_sel); wt->wcol_theme = &wcol_menu_option; - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); wt->wcol_theme = old_wcol; } static void widget_state_nothing(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2653,8 +2666,7 @@ static void widget_state_nothing(uiWidgetType *wt, /* special case, button that calls pulldown */ static void widget_state_pulldown(uiWidgetType *wt, - int UNUSED(state), - int UNUSED(drawflag), + const uiWidgetStateInfo *UNUSED(state), eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); @@ -2662,14 +2674,13 @@ static void widget_state_pulldown(uiWidgetType *wt, /* special case, pie menu items */ static void widget_state_pie_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f); /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ @@ -2678,18 +2689,18 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & (UI_SELECT | UI_ACTIVE)) { + if (state->but_flag & (UI_SELECT | UI_ACTIVE)) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } - else if (state & UI_ACTIVE) { + else if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item); } } @@ -2697,14 +2708,13 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, - int state, - int UNUSED(drawflag), + const uiWidgetStateInfo *state, eUIEmbossType UNUSED(emboss)) { wt->wcol = *(wt->wcol_theme); /* active and disabled (not so common) */ - if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_ACTIVE)) { /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ wt->wcol.text[3] = 128; @@ -2713,15 +2723,15 @@ static void widget_state_menu_item(uiWidgetType *wt, } else { /* regular active */ - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); } - else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f); } - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); } } @@ -2787,7 +2797,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r } static void widget_menu_back( - uiWidgetColors *wcol, rcti *rect, int flag, int direction, const float zoom) + uiWidgetColors *wcol, rcti *rect, const int block_flag, const int direction, const float zoom) { uiWidgetBase wtb; int roundboxalign = UI_CNR_ALL; @@ -2795,7 +2805,7 @@ static void widget_menu_back( widget_init(&wtb); /* menu is 2nd level or deeper */ - if (flag & UI_BLOCK_POPUP) { + if (block_flag & UI_BLOCK_POPUP) { // rect->ymin -= 4.0; // rect->ymax += 4.0; } @@ -3321,13 +3331,17 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) #define NUM_BUT_PADDING_FACTOR 0.425f -static void widget_numbut_draw( - uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss) +static void widget_numbut_draw(uiWidgetColors *wcol, + rcti *rect, + const float zoom, + const uiWidgetStateInfo *state, + int roundboxalign, + bool emboss) { const float rad = widget_radius_from_zoom(zoom, wcol); const int handle_width = min_ii(BLI_rcti_size_x(rect) / 3, BLI_rcti_size_y(rect) * 0.7f); - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3343,7 +3357,7 @@ static void widget_numbut_draw( } /* decoration */ - if ((state & UI_ACTIVE) && !(state & UI_STATE_TEXT_INPUT)) { + if ((state->but_flag & UI_ACTIVE) && !state->is_text_input) { uiWidgetColors wcol_zone; uiWidgetBase wtb_zone; rcti rect_zone; @@ -3356,7 +3370,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_LEFT) { + if (state->but_drawflag & UI_BUT_ACTIVE_LEFT) { widget_active_color(&wcol_zone); } @@ -3376,7 +3390,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (state & UI_STATE_ACTIVE_RIGHT) { + if (state->but_drawflag & UI_BUT_ACTIVE_RIGHT) { widget_active_color(&wcol_zone); } @@ -3395,7 +3409,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); - if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) { + if (!(state->but_drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT))) { widget_active_color(&wcol_zone); } @@ -3414,7 +3428,7 @@ static void widget_numbut_draw( widgetbase_draw(&wtb, wcol); } - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmin += text_padding; @@ -3422,14 +3436,20 @@ static void widget_numbut_draw( } } -static void widget_numbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { widget_numbut_draw(wcol, rect, zoom, state, roundboxalign, false); } -static void widget_menubut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menubut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3454,7 +3474,7 @@ static void widget_menubut( static void widget_menubut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3476,7 +3496,7 @@ static void widget_menubut_embossn(const uiBut *UNUSED(but), static void widget_numbut_embossn(const uiBut *UNUSED(but), uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int roundboxalign, const float zoom) { @@ -3486,7 +3506,6 @@ static void widget_numbut_embossn(const uiBut *UNUSED(but), void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *slider, int state) { uiWidgetBase wtb; - bool outline = false; widget_init(&wtb); @@ -3531,11 +3550,6 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s /* draw */ wtb.draw_emboss = false; /* only emboss once */ - /* exception for progress bar */ - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } - round_box_edges(&wtb, UI_CNR_ALL, slider, rad); if (state & UI_SCROLL_ARROWS) { @@ -3563,17 +3577,13 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s } } widgetbase_draw(&wtb, wcol); - - if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.draw_outline); - } } } static void widget_scroll(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3623,19 +3633,13 @@ static void widget_scroll(uiBut *but, } } - if (state & UI_SELECT) { - state = UI_SCROLL_PRESSED; - } - else { - state = 0; - } - UI_draw_widget_scroll(wcol, rect, &rect1, state); + UI_draw_widget_scroll(wcol, rect, &rect1, (state->but_flag & UI_SELECT) ? UI_SCROLL_PRESSED : 0); } static void widget_progressbar(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -3669,7 +3673,7 @@ static void widget_progressbar(uiBut *but, static void widget_treerow_exec(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), int indentation, const float zoom) @@ -3682,7 +3686,7 @@ static void widget_treerow_exec(uiWidgetColors *wcol, const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); - if ((state & UI_ACTIVE) || (state & UI_SELECT)) { + if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } @@ -3690,8 +3694,12 @@ static void widget_treerow_exec(uiWidgetColors *wcol, BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); } -static void widget_treerow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_treerow(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiButTreeRow *tree_row = (uiButTreeRow *)but; BLI_assert(but->type == UI_BTYPE_TREEROW); @@ -3701,7 +3709,7 @@ static void widget_treerow( static void widget_nodesocket(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3737,8 +3745,12 @@ static void widget_nodesocket(uiBut *but, copy_v3_v3_uchar(wcol->outline, old_outline); } -static void widget_numslider( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_numslider(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb, wtb1; widget_init(&wtb); @@ -3752,7 +3764,7 @@ static void widget_numslider( widgetbase_draw(&wtb, wcol); /* Draw slider part only when not in text editing. */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { int roundboxalign_slider = roundboxalign; uchar outline[3]; @@ -3760,7 +3772,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, wcol->item); copy_v3_v3_uchar(wcol->inner, wcol->item); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3828,7 +3840,7 @@ static void widget_numslider( copy_v3_v3_uchar(wcol->outline, outline); - if (!(state & UI_SELECT)) { + if (!(state->but_flag & UI_SELECT)) { SWAP(short, wcol->shadetop, wcol->shadedown); } } @@ -3840,7 +3852,7 @@ static void widget_numslider( /* Add space at either side of the button so text aligns with number-buttons * (which have arrow icons). */ - if (!(state & UI_STATE_TEXT_INPUT)) { + if (!state->is_text_input) { const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); rect->xmax -= text_padding; rect->xmin += text_padding; @@ -3850,8 +3862,12 @@ static void widget_numslider( /* I think 3 is sufficient border to indicate keyed status */ #define SWATCH_KEYED_BORDER 3 -static void widget_swatch( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_swatch(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { BLI_assert(but->type == UI_BTYPE_COLOR); uiButColor *color_but = (uiButColor *)but; @@ -3875,9 +3891,9 @@ static void widget_swatch( ui_but_v3_get(but, col); - if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN | - UI_BUT_REDALERT)) || - (but->drawflag & UI_BUT_ANIMATED_CHANGED)) { + if ((state->but_flag & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | + UI_BUT_OVERRIDDEN | UI_BUT_REDALERT)) || + (state->but_drawflag & UI_BUT_ANIMATED_CHANGED)) { /* draw based on state - color for keyed etc */ widgetbase_draw(&wtb, wcol); @@ -3938,7 +3954,7 @@ static void widget_swatch( static void widget_unitvec(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -3946,10 +3962,15 @@ static void widget_unitvec(uiBut *but, ui_draw_but_UNITVEC(but, wcol, rect, rad); } -static void widget_icon_has_anim( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_icon_has_anim(uiBut *but, + uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && + if (state->but_flag & + (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_REDALERT) && but->emboss != UI_EMBOSS_NONE) { uiWidgetBase wtb; widget_init(&wtb); @@ -3970,10 +3991,13 @@ static void widget_icon_has_anim( } } -static void widget_textbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_textbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { SWAP(short, wcol->shadetop, wcol->shadedown); } @@ -3989,7 +4013,7 @@ static void widget_textbut( static void widget_preview_tile(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float UNUSED(zoom)) { @@ -3998,8 +4022,11 @@ static void widget_preview_tile(uiBut *but, &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); } -static void widget_menuiconbut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_menuiconbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4011,17 +4038,20 @@ static void widget_menuiconbut( widgetbase_draw(&wtb, wcol); } -static void widget_pulldownbut( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_pulldownbut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { float back[4]; UI_GetThemeColor4fv(TH_BACK, back); - if ((state & UI_ACTIVE) || (back[3] < 1.0f)) { + if ((state->but_flag & UI_ACTIVE) || (back[3] < 1.0f)) { uiWidgetBase wtb; const float rad = widget_radius_from_zoom(zoom, wcol); - if (state & UI_ACTIVE) { + if (state->but_flag & UI_ACTIVE) { copy_v4_v4_uchar(wcol->inner, wcol->inner_sel); copy_v3_v3_uchar(wcol->text, wcol->text_sel); copy_v3_v3_uchar(wcol->outline, wcol->inner); @@ -4042,7 +4072,7 @@ static void widget_pulldownbut( static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4066,7 +4096,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol, static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4088,7 +4118,7 @@ static void widget_menu_itembut_unpadded(uiWidgetColors *wcol, static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4114,7 +4144,7 @@ static void widget_menu_radial_itembut(uiBut *but, static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int UNUSED(roundboxalign), const float zoom) { @@ -4131,11 +4161,13 @@ static void widget_list_itembut(uiWidgetColors *wcol, static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, - int state, + const uiWidgetStateInfo *state, int UNUSED(roundboxalign), const float UNUSED(zoom)) { - const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); + /* For a right aligned layout (signified by #UI_BUT_TEXT_RIGHT), draw the text on the left of the + * checkbox. */ + const bool text_before_widget = (state->but_drawflag & UI_BUT_TEXT_RIGHT); rcti recttemp = *rect; uiWidgetBase wtb; @@ -4160,7 +4192,7 @@ static void widget_optionbut(uiWidgetColors *wcol, round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad); /* decoration */ - if (state & UI_SELECT) { + if (state->but_flag & UI_SELECT) { shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } @@ -4177,19 +4209,21 @@ static void widget_optionbut(uiWidgetColors *wcol, } /* labels use Editor theme colors for text */ -static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmbossType emboss) +static void widget_state_label(uiWidgetType *wt, + const uiWidgetStateInfo *state, + eUIEmbossType emboss) { - if (state & UI_BUT_LIST_ITEM) { + if (state->but_flag & UI_BUT_LIST_ITEM) { /* Override default label theme's colors. */ bTheme *btheme = UI_GetTheme(); wt->wcol_theme = &btheme->tui.wcol_list_item; /* call this for option button */ - widget_state(wt, state, drawflag, emboss); + widget_state(wt, state, emboss); } else { /* call this for option button */ - widget_state(wt, state, drawflag, emboss); - if (state & UI_SELECT) { + widget_state(wt, state, emboss); + if (state->but_flag & UI_SELECT) { UI_GetThemeColor3ubv(TH_TEXT_HI, wt->wcol.text); } else { @@ -4197,14 +4231,17 @@ static void widget_state_label(uiWidgetType *wt, int state, int drawflag, eUIEmb } } - if (state & UI_BUT_REDALERT) { + if (state->but_flag & UI_BUT_REDALERT) { const uchar red[4] = {255, 0, 0}; color_blend_v3_v3(wt->wcol.text, red, 0.4f); } } -static void widget_radiobut( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_radiobut(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4218,7 +4255,7 @@ static void widget_radiobut( static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, - int UNUSED(state), + const uiWidgetStateInfo *UNUSED(state), int roundboxalign, const float zoom) { @@ -4244,8 +4281,11 @@ static void widget_box(uiBut *but, copy_v3_v3_uchar(wcol->inner, old_col); } -static void widget_but( - uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign, const float zoom) +static void widget_but(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *UNUSED(state), + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -4271,13 +4311,16 @@ static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), } #endif -static void widget_roundbut_exec( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_roundbut_exec(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); - if (state & UI_STATE_HOLD_ACTION) { + if (state->has_hold_action) { /* Show that keeping pressed performs another action (typically a menu). */ shape_preset_init_hold_action(&wtb.tria1, rect, 0.75f, 'r'); } @@ -4290,11 +4333,14 @@ static void widget_roundbut_exec( widgetbase_draw(&wtb, wcol); } -static void widget_tab( - uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign, const float zoom) +static void widget_tab(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int roundboxalign, + const float zoom) { const float rad = widget_radius_from_zoom(zoom, wcol); - const bool is_active = (state & UI_SELECT); + const bool is_active = (state->but_flag & UI_SELECT); /* Draw shaded outline - Disabled for now, * seems incorrect and also looks nicer without it IMHO ;). */ @@ -4442,7 +4488,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_TOOLTIP: wt.wcol_theme = &btheme->tui.wcol_tooltip; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* strings */ @@ -4498,7 +4544,7 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) case UI_WTYPE_MENU_BACK: wt.wcol_theme = &btheme->tui.wcol_menu_back; - wt.draw = widget_menu_back; + wt.draw_block = widget_menu_back; break; /* specials */ @@ -4921,62 +4967,50 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu const int roundboxalign = widget_roundbox_set(but, rect); - /* Mask out flags re-used for local state. */ - int state = but->flag & ~UI_STATE_FLAGS_ALL; - const int drawflag = but->drawflag; + uiWidgetStateInfo state = {0}; + state.but_flag = but->flag; + state.but_drawflag = but->drawflag; - if (state & UI_SELECT_DRAW) { - state |= UI_SELECT; + /* Override selected flag for drawing. */ + if (but->flag & UI_SELECT_DRAW) { + state.but_flag |= UI_SELECT; } if ((but->editstr) || (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI) && ui_but_drag_multi_edit_get(but))) { - state |= UI_STATE_TEXT_INPUT; + state.is_text_input = true; } if (but->hold_func) { - state |= UI_STATE_HOLD_ACTION; - } - - if (state & UI_ACTIVE) { - if (but->drawflag & UI_BUT_ACTIVE_LEFT) { - state |= UI_STATE_ACTIVE_LEFT; - } - else if (but->drawflag & UI_BUT_ACTIVE_RIGHT) { - state |= UI_STATE_ACTIVE_RIGHT; - } + state.has_hold_action = true; } bool use_alpha_blend = false; if (but->emboss != UI_EMBOSS_PULLDOWN) { - if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { + if (but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE | UI_SEARCH_FILTER_NO_MATCH)) { use_alpha_blend = true; - ui_widget_color_disabled(wt, state); + ui_widget_color_disabled(wt, &state); } } - if (drawflag & UI_BUT_TEXT_RIGHT) { - state |= UI_STATE_TEXT_BEFORE_WIDGET; - } - #ifdef USE_UI_POPOVER_ONCE if (but->block->flag & UI_BLOCK_POPOVER_ONCE) { - if ((state & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { - state |= UI_BUT_ACTIVE_DEFAULT; + if ((but->flag & UI_ACTIVE) && ui_but_is_popover_once_compat(but)) { + state.but_flag |= UI_BUT_ACTIVE_DEFAULT; } } #endif if (but->block->flag & UI_BLOCK_NO_DRAW_OVERRIDDEN_STATE) { - state &= ~UI_BUT_OVERRIDDEN; + state.but_flag &= ~UI_BUT_OVERRIDDEN; } const float zoom = 1.0f / but->block->aspect; - wt->state(wt, state, drawflag, but->emboss); + wt->state(wt, &state, but->emboss); if (wt->custom) { - wt->custom(but, &wt->wcol, rect, state, roundboxalign, zoom); + wt->custom(but, &wt->wcol, rect, &state, roundboxalign, zoom); } else if (wt->draw) { - wt->draw(&wt->wcol, rect, state, roundboxalign, zoom); + wt->draw(&wt->wcol, rect, &state, roundboxalign, zoom); } if (wt->text) { @@ -5017,13 +5051,13 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_BACK); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (block) { const float zoom = 1.0f / block->aspect; - wt->draw(&wt->wcol, rect, block->flag, block->direction, zoom); + wt->draw_block(&wt->wcol, rect, block->flag, block->direction, zoom); } else { - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } ui_draw_clip_tri(block, rect, wt); @@ -5118,8 +5152,8 @@ void ui_draw_popover_back(struct ARegion *region, } else { const float zoom = 1.0f / block->aspect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, zoom); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + wt->draw_block(&wt->wcol, rect, 0, 0, zoom); } ui_draw_clip_tri(block, rect, wt); @@ -5307,11 +5341,20 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type, } rcti rect_copy = *rect; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); if (color) { rgba_float_to_uchar(wt->wcol.inner, color); } - wt->draw(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + + if (wt->draw_block) { + wt->draw_block(&wt->wcol, &rect_copy, 0, UI_CNR_ALL, 1.0f); + } + else if (wt->draw) { + wt->draw(&wt->wcol, &rect_copy, &STATE_INFO_NULL, UI_CNR_ALL, 1.0f); + } + else { + BLI_assert_unreachable(); + } } void ui_draw_widget_menu_back_color(const rcti *rect, bool use_shadow, const float color[4]) { @@ -5326,16 +5369,16 @@ void ui_draw_widget_menu_back(const rcti *rect, bool use_shadow) void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(block), rcti *rect) { uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - /* wt->draw ends up using same function to draw the tooltip as menu_back */ - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &STATE_INFO_NULL, UI_EMBOSS_UNDEFINED); + /* wt->draw_block ends up using same function to draw the tooltip as menu_back */ + wt->draw_block(&wt->wcol, rect, 0, 0, 1.0f); } void ui_draw_menu_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, uiMenuItemSeparatorType separator_type, int *r_xmax) { @@ -5346,8 +5389,11 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, int padding = 0.25f * row_height; char *cpoin = NULL; - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); UI_fontstyle_set(fstyle); @@ -5442,8 +5488,12 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, /* part text right aligned */ if (separator_type != UI_MENU_ITEM_SEPARATOR_NONE) { if (cpoin) { + /* State info for the hint drawing. */ + uiWidgetStateInfo hint_state = state; /* Set inactive state for grayed out text. */ - wt->state(wt, state | UI_BUT_INACTIVE, 0, UI_EMBOSS_UNDEFINED); + hint_state.but_flag |= UI_BUT_INACTIVE; + + wt->state(wt, &hint_state, UI_EMBOSS_UNDEFINED); char hint_drawstr[UI_MAX_DRAW_STR]; { @@ -5528,14 +5578,17 @@ void ui_draw_preview_item(const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, - int state, + int but_flag, eFontStyle_Align text_align) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM_UNPADDED); + uiWidgetStateInfo state = {0}; + state.but_flag = but_flag; + /* drawing button background */ - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0, 1.0f); + wt->state(wt, &state, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, &STATE_INFO_NULL, 0, 1.0f); ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align); } diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index 66171dc13e4..6ece7eb4ffa 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -2103,21 +2103,12 @@ void UI_view2d_text_cache_draw(ARegion *region) col_pack_prev = v2s->col.pack; } - if (v2s->rect.xmin >= v2s->rect.xmax) { - BLF_draw_default((float)(v2s->mval[0] + xofs), - (float)(v2s->mval[1] + yofs), - 0.0, - v2s->str, - BLF_DRAW_STR_DUMMY_MAX); - } - else { - BLF_enable(font_id, BLF_CLIPPING); - BLF_clipping( - font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); - BLF_draw_default( - v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); - BLF_disable(font_id, BLF_CLIPPING); - } + BLF_enable(font_id, BLF_CLIPPING); + BLF_clipping( + font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); + BLF_draw_default( + v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX); + BLF_disable(font_id, BLF_CLIPPING); } g_v2d_strings = nullptr; diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.cc b/source/blender/editors/interface/view2d_gizmo_navigate.cc index 01729e35246..fae28833e4f 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.cc +++ b/source/blender/editors/interface/view2d_gizmo_navigate.cc @@ -130,6 +130,13 @@ static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSE } break; } + case SPACE_IMAGE: { + const SpaceImage *sima = static_cast<const SpaceImage *>(area->spacedata.first); + if (sima->gizmo_flag & (SI_GIZMO_HIDE | SI_GIZMO_HIDE_NAVIGATE)) { + return false; + } + break; + } } return true; } diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index fd454083653..87923d9fdf8 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -282,6 +282,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_alembic_export_draw; ot->check = wm_alembic_export_check; + ot->flag |= OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, @@ -475,7 +476,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) /* This dummy prop is used to check whether we need to init the start and * end frame values to that of the scene's, otherwise they are reset at * every change, draw update. */ - RNA_def_boolean(ot->srna, "init_scene_frame_range", false, "", ""); + RNA_def_boolean(ot->srna, "init_scene_frame_range", true, "", ""); } /* ************************************************************************** */ diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c index 624b85358f5..969049313c9 100644 --- a/source/blender/editors/io/io_cache.c +++ b/source/blender/editors/io/io_cache.c @@ -75,17 +75,17 @@ static void open_cancel(bContext *UNUSED(C), wmOperator *op) static int cachefile_open_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); Main *bmain = CTX_data_main(C); - CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename), 0); - BLI_strncpy(cache_file->filepath, filename, FILE_MAX); + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filepath), 0); + BLI_strncpy(cache_file->filepath, filepath, FILE_MAX); DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE); /* Will be set when running invoke, not exec directly. */ @@ -182,7 +182,7 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven static int cachefile_layer_add_exec(bContext *C, wmOperator *op) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); + BKE_report(op->reports, RPT_ERROR, "No filepath given"); return OPERATOR_CANCELLED; } @@ -192,10 +192,10 @@ static int cachefile_layer_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); - CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filename); + CacheFileLayer *layer = BKE_cachefile_add_layer(cache_file, filepath); if (!layer) { WM_report(RPT_ERROR, "Could not add a layer to the cache file"); diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 886586ff236..5537b2575d8 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -16,6 +16,8 @@ #include "BLT_translation.h" +#include "ED_outliner.h" + #include "MEM_guardedalloc.h" #include "RNA_access.h" @@ -410,6 +412,12 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) OBJ_import(C, &import_params); + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -448,6 +456,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; ot->invoke = wm_obj_import_invoke; ot->exec = wm_obj_import_exec; diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index ca9c2de63a4..609230eefea 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -118,7 +118,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface"); const bool export_textures = RNA_boolean_get(op->ptr, "export_textures"); const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures"); - const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths"); + const bool relative_paths = RNA_boolean_get(op->ptr, "relative_paths"); struct USDExportParams params = { export_animation, @@ -133,7 +133,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op) generate_preview_surface, export_textures, overwrite_textures, - relative_texture_paths, + relative_paths, }; bool ok = USD_export(C, filename, ¶ms, as_background_job); @@ -181,9 +181,9 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) const bool export_tex = RNA_boolean_get(ptr, "export_textures"); uiLayoutSetActive(row, export_mtl && preview && export_tex); - row = uiLayoutRow(col, true); - uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE); - uiLayoutSetActive(row, export_mtl && preview); + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("File References")); + uiItemR(col, ptr, "relative_paths", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); uiItemL(box, IFACE_("Experimental"), ICON_NONE); @@ -297,10 +297,11 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "Allow overwriting existing texture files when exporting textures"); RNA_def_boolean(ot->srna, - "relative_texture_paths", + "relative_paths", true, - "Relative Texture Paths", - "Make texture asset paths relative to the USD file"); + "Relative Paths", + "Use relative paths to reference external files (i.e. textures, volumes) in " + "USD, otherwise use absolute paths"); } /* ====== USD Import ====== */ diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 0b9261eac4f..d10c420e28c 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -230,7 +230,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float tangent[2]; float u; @@ -593,7 +593,7 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op) MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; - const float threshold = 9; + const float threshold = 12; float co[2], u; RNA_float_get_array(op->ptr, "location", co); diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index afe457a8502..89524a7b9e2 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -45,6 +45,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, float *r_u, float *r_score) { + const float threshold_sq = threshold * threshold; + ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -139,7 +141,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } } - if (point && dist_best_sq < threshold) { + if (point && dist_best_sq < threshold_sq) { if (r_mask_layer) { *r_mask_layer = point_mask_layer; } diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index ed09e5a6334..28ac913a3e3 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -24,7 +24,7 @@ set(INC ) set(SRC - editface.c + editface.cc editmesh_add.c editmesh_add_gizmo.c editmesh_automerge.c @@ -51,10 +51,10 @@ set(SRC editmesh_tools.c editmesh_undo.c editmesh_utils.c - mesh_data.c + mesh_data.cc mesh_mirror.c mesh_ops.c - meshtools.c + meshtools.cc mesh_intern.h ) diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.cc index a5c6adaa43e..cb5d48d1023 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.cc @@ -36,16 +36,16 @@ /* own include */ -void paintface_flush_flags(struct bContext *C, Object *ob, short flag) +void paintface_flush_flags(bContext *C, Object *ob, short flag) { Mesh *me = BKE_mesh_from_object(ob); MPoly *polys, *mp_orig; - const int *index_array = NULL; + const int *index_array = nullptr; int totpoly; BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0); - if (me == NULL) { + if (me == nullptr) { return; } @@ -60,7 +60,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - if (ob_eval == NULL) { + if (ob_eval == nullptr) { return; } @@ -68,7 +68,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; bool updated = false; - if (me_orig != NULL && me_eval != NULL && me_orig->totpoly == me->totpoly) { + if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) { /* Update the COW copy of the mesh. */ for (int i = 0; i < me->totpoly; i++) { me_orig->mpoly[i].flag = me->mpoly[i].flag; @@ -79,7 +79,7 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) updated = true; } /* Mesh polys => Final derived polys */ - else if ((index_array = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { + else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { polys = me_eval->mpoly; totpoly = me_eval->totpoly; @@ -104,10 +104,10 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_SELECT_PAINT); } - DEG_id_tag_update(ob->data, ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT); } else { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); @@ -115,18 +115,13 @@ void paintface_flush_flags(struct bContext *C, Object *ob, short flag) void paintface_hide(bContext *C, Object *ob, const bool unselected) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) { mpoly->flag |= ME_HIDE; @@ -136,8 +131,6 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) if (mpoly->flag & ME_HIDE) { mpoly->flag &= ~ME_FACE_SEL; } - - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -147,23 +140,17 @@ void paintface_hide(bContext *C, Object *ob, const bool unselected) void paintface_reveal(bContext *C, Object *ob, const bool select) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if (mpoly->flag & ME_HIDE) { SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL); mpoly->flag &= ~ME_HIDE; } - mpoly++; } BKE_mesh_flush_hidden_from_polys(me); @@ -175,9 +162,6 @@ void paintface_reveal(bContext *C, Object *ob, const bool select) static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) { - MPoly *mp; - MLoop *ml; - int a, b; bool do_it = true; bool mark = false; @@ -186,20 +170,20 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo if (index != (uint)-1) { /* only put face under cursor in array */ - mp = &me->mpoly[index]; + MPoly *mp = &me->mpoly[index]; BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); BLI_BITMAP_ENABLE(poly_tag, index); } else { /* fill array by selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); } } } @@ -208,17 +192,17 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo do_it = false; /* expand selection */ - mp = me->mpoly; - for (a = 0; a < me->totpoly; a++, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE) { continue; } - if (!BLI_BITMAP_TEST(poly_tag, a)) { + if (!BLI_BITMAP_TEST(poly_tag, i)) { mark = false; - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { if ((me->medge[ml->e].flag & ME_SEAM) == 0) { if (BLI_BITMAP_TEST(edge_tag, ml->e)) { mark = true; @@ -228,7 +212,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo } if (mark) { - BLI_BITMAP_ENABLE(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, i); BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); do_it = true; } @@ -238,8 +222,9 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo MEM_freeN(edge_tag); - for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) { - if (BLI_BITMAP_TEST(poly_tag, a)) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; + if (BLI_BITMAP_TEST(poly_tag, i)) { SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL); } } @@ -249,11 +234,10 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const bool select) { - Mesh *me; uint index = (uint)-1; - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { return; } @@ -270,34 +254,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags) { - Mesh *me; - MPoly *mpoly; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) { action = SEL_DESELECT; break; } - mpoly++; } } bool changed = false; - mpoly = me->mpoly; - a = me->totpoly; - while (a--) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; if ((mpoly->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -318,7 +295,6 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl break; } } - mpoly++; } if (changed) { @@ -331,30 +307,25 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { - const Mesh *me; - const MPoly *mp; - const MLoop *ml; - const MVert *mvert; - int a, b; bool ok = false; float vec[3], bmat[3][3]; - me = BKE_mesh_from_object(ob); + const Mesh *me = BKE_mesh_from_object(ob); if (!me || !me->mloopuv) { return ok; } + const MVert *mvert = me->mvert; copy_m3_m4(bmat, ob->obmat); - mvert = me->mvert; - mp = me->mpoly; - for (a = me->totpoly; a > 0; a--, mp++) { + for (int i = 0; i < me->totpoly; i++) { + MPoly *mp = &me->mpoly[i]; if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) { continue; } - ml = me->mloop + mp->loopstart; - for (b = 0; b < mp->totloop; b++, ml++) { + const MLoop *ml = me->mloop + mp->loopstart; + for (int b = 0; b < mp->totloop; b++, ml++) { mul_v3_m3v3(vec, bmat, mvert[ml->v].co); add_v3_v3v3(vec, vec, ob->obmat[3]); minmax_v3v3_v3(r_min, r_max, vec); @@ -366,19 +337,18 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) return ok; } -bool paintface_mouse_select(struct bContext *C, +bool paintface_mouse_select(bContext *C, const int mval[2], - const struct SelectPick_Params *params, + const SelectPick_Params *params, Object *ob) { - Mesh *me; - MPoly *mpoly_sel = NULL; + MPoly *mpoly_sel = nullptr; uint index; bool changed = false; bool found = false; /* Get the face under the cursor */ - me = BKE_mesh_from_object(ob); + Mesh *me = BKE_mesh_from_object(ob); if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { if (index < me->totpoly) { @@ -444,11 +414,11 @@ void paintvert_flush_flags(Object *ob) Mesh *me = BKE_mesh_from_object(ob); Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); MVert *mvert_eval, *mv; - const int *index_array = NULL; + const int *index_array = nullptr; int totvert; int i; - if (me == NULL) { + if (me == nullptr) { return; } @@ -456,11 +426,11 @@ void paintvert_flush_flags(Object *ob) * since this could become slow for realtime updates (circle-select for eg) */ BKE_mesh_flush_select_from_verts(me); - if (me_eval == NULL) { + if (me_eval == nullptr) { return; } - index_array = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); mvert_eval = me_eval->mvert; totvert = me_eval->totvert; @@ -485,41 +455,34 @@ void paintvert_flush_flags(Object *ob) BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL); } -void paintvert_tag_select_update(struct bContext *C, struct Object *ob) +void paintvert_tag_select_update(bContext *C, Object *ob) { - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); } bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) { - Mesh *me; - MVert *mvert; - int a; - - me = BKE_mesh_from_object(ob); - if (me == NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me == nullptr) { return false; } if (action == SEL_TOGGLE) { action = SEL_SELECT; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) { action = SEL_DESELECT; break; } - mvert++; } } bool changed = false; - mvert = me->mvert; - a = me->totvert; - while (a--) { + for (int i = 0; i < me->totvert; i++) { + MVert *mvert = &me->mvert[i]; if ((mvert->flag & ME_HIDE) == 0) { switch (action) { case SEL_SELECT: @@ -540,7 +503,6 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) break; } } - mvert++; } if (changed) { @@ -565,11 +527,8 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) { Mesh *me = BKE_mesh_from_object(ob); - MVert *mv; - MDeformVert *dv; - int a, tot; - if (me == NULL || me->dvert == NULL) { + if (me == nullptr || me->dvert == nullptr) { return; } @@ -577,12 +536,11 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } - dv = me->dvert; - tot = me->totvert; - - for (a = 0, mv = me->mvert; a < tot; a++, mv++, dv++) { + for (int i = 0; i < me->totvert; i++) { + MVert *mv = &me->mvert[i]; + MDeformVert *dv = &me->dvert[i]; if ((mv->flag & ME_HIDE) == 0) { - if (dv->dw == NULL) { + if (dv->dw == nullptr) { /* if null weight then not grouped */ mv->flag |= SELECT; } diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index b2f33428b21..57e0d04727c 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -113,7 +113,7 @@ static int add_primitive_plane_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -178,7 +178,7 @@ static int add_primitive_cube_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -252,7 +252,7 @@ static int add_primitive_circle_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -324,7 +324,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -400,7 +400,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -476,7 +476,7 @@ static int add_primitive_grid_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -553,7 +553,7 @@ static int add_primitive_monkey_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, @@ -614,7 +614,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( @@ -682,7 +682,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op) em = BKE_editmesh_from_object(obedit); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf( diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index d0f37314661..f5090c0143d 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -328,7 +328,7 @@ static int add_primitive_cube_gizmo_exec(bContext *C, wmOperator *op) const bool calc_uvs = RNA_boolean_get(op->ptr, "calc_uvs"); if (calc_uvs) { - ED_mesh_uv_texture_ensure(obedit->data, NULL); + ED_mesh_uv_ensure(obedit->data, NULL); } if (!EDBM_op_call_and_selectf(em, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 01276d7640d..5b3487b0a33 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -198,7 +198,7 @@ typedef struct KnifeObjectInfo { * Optionally allocate triangle indices, these are needed for non-interactive knife * projection as multiple cuts are made without the BVH being updated. * Using these indices the it's possible to access `cagecos` even if the face has been cut - * and the loops in `em->looptris` no longer refer to the original triangles, see: + * and the loops in `em->looptris` no longer refer to the original triangles, see: T97153. */ const int (*tri_indices)[3]; @@ -377,45 +377,45 @@ static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); /* Ray-cast all planes. */ + float ray_dir[3]; + float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; + float lambda_best[2] = {-FLT_MAX, FLT_MAX}; + int i; + { - float ray_dir[3]; - float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; - float lambda_best[2] = {-FLT_MAX, FLT_MAX}; - int i; + float curr_cage_adjust[3]; + float co_depth[3]; - { - float curr_cage_adjust[3]; - float co_depth[3]; + copy_v3_v3(co_depth, kcd->prev.cage); + ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - copy_v3_v3(co_depth, kcd->prev.cage); - ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); + sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + } - sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); + for (i = 0; i < 4; i++) { + float ray_hit[3]; + float lambda_test; + if (!isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { + continue; } - for (i = 0; i < 4; i++) { - float ray_hit[3]; - float lambda_test; - if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) { - madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); - if (lambda_test < 0.0f) { - if (lambda_test > lambda_best[0]) { - copy_v3_v3(ray_hit_best[0], ray_hit); - lambda_best[0] = lambda_test; - } - } - else { - if (lambda_test < lambda_best[1]) { - copy_v3_v3(ray_hit_best[1], ray_hit); - lambda_best[1] = lambda_test; - } - } + madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test); + if (lambda_test < 0.0f) { + if (lambda_test > lambda_best[0]) { + copy_v3_v3(ray_hit_best[0], ray_hit); + lambda_best[0] = lambda_test; + } + } + else { + if (lambda_test < lambda_best[1]) { + copy_v3_v3(ray_hit_best[1], ray_hit); + lambda_best[1] = lambda_test; } } - - copy_v3_v3(r_v1, ray_hit_best[0]); - copy_v3_v3(r_v2, ray_hit_best[1]); } + + copy_v3_v3(r_v1, ray_hit_best[0]); + copy_v3_v3(r_v2, ray_hit_best[1]); } static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) @@ -440,43 +440,45 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) { - if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { - float v1[3], v2[3]; + if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { + return; + } - /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ - knifetool_raycast_planes(kcd, v1, v2); + float v1[3], v2[3]; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ + knifetool_raycast_planes(kcd, v1, v2); - switch (kcd->constrain_axis) { - case KNF_CONSTRAIN_AXIS_X: { - immUniformColor3ubv(kcd->colors.xaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Y: { - immUniformColor3ubv(kcd->colors.yaxis); - break; - } - case KNF_CONSTRAIN_AXIS_Z: { - immUniformColor3ubv(kcd->colors.zaxis); - break; - } - default: { - immUniformColor3ubv(kcd->colors.axis_extra); - break; - } + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + switch (kcd->constrain_axis) { + case KNF_CONSTRAIN_AXIS_X: { + immUniformColor3ubv(kcd->colors.xaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Y: { + immUniformColor3ubv(kcd->colors.yaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Z: { + immUniformColor3ubv(kcd->colors.zaxis); + break; + } + default: { + immUniformColor3ubv(kcd->colors.axis_extra); + break; } + } - GPU_line_width(2.0); + GPU_line_width(2.0); - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, v1); - immVertex3fv(pos, v2); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); - immUnbindProgram(); - } + immUnbindProgram(); } static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) @@ -1226,7 +1228,6 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) } /* Construct BVH Tree. */ - float cos[3][3]; const float epsilon = FLT_EPSILON * 2.0f; int tottri = 0; int ob_tottri = 0; @@ -1283,8 +1284,10 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) if (!test_fn_ret) { continue; } - knife_bm_tri_cagecos_get_worldspace(kcd, b, i, cos); - BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); + + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, tri_cos); + BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3); } tottri += em->tottri; @@ -1307,6 +1310,10 @@ static void knife_bvh_raycast_cb(void *userdata, const BVHTreeRay *ray, BVHTreeRayHit *hit) { + if (index == -1) { + return; + } + KnifeTool_OpData *kcd = userdata; BMLoop **ltri; Object *ob; @@ -1315,60 +1322,49 @@ static void knife_bvh_raycast_cb(void *userdata, float dist, uv[2]; bool isect; int tottri; - float tri_cos[3][3]; - if (index != -1) { - tottri = 0; - uint b = 0; - for (; b < kcd->objects_len; b++) { - index -= tottri; - ob = kcd->objects[b]; - em = BKE_editmesh_from_object(ob); - tottri = em->tottri; - if (index < tottri) { - ltri = em->looptris[index]; - break; - } + tottri = 0; + uint b = 0; + for (; b < kcd->objects_len; b++) { + index -= tottri; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + tottri = em->tottri; + if (index < tottri) { + ltri = em->looptris[index]; + break; } + } - if (kcd->bvh.filter_cb) { - if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { - return; - } + if (kcd->bvh.filter_cb) { + if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { + return; } + } - knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); - - isect = - (ray->radius > 0.0f ? - isect_ray_tri_epsilon_v3(ray->origin, - ray->direction, - tri_cos[0], - tri_cos[1], - tri_cos[2], - &dist, - uv, - ray->radius) : + float tri_cos[3][3]; + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); + isect = (ray->radius > 0.0f ? + isect_ray_tri_epsilon_v3( + ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv, ray->radius) : #ifdef USE_KDOPBVH_WATERTIGHT - isect_ray_tri_watertight_v3( - ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, uv)); #else - isect_ray_tri_v3( - ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); + isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, uv); #endif - if (isect && dist < hit->dist) { - hit->dist = dist; - hit->index = index; + if (isect && dist < hit->dist) { + hit->dist = dist; + hit->index = index; - copy_v3_v3(hit->no, ltri[0]->f->no); + copy_v3_v3(hit->no, ltri[0]->f->no); - madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); - kcd->bvh.looptris = em->looptris; - copy_v2_v2(kcd->bvh.uv, uv); - kcd->bvh.base_index = b; - } + kcd->bvh.looptris = em->looptris; + copy_v2_v2(kcd->bvh.uv, uv); + kcd->bvh.base_index = b; } } @@ -1978,29 +1974,30 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) * Also remove all but one of a series of vertex hits for the same vertex. */ for (int i = 0; i < n; i++) { KnifeLineHit *lhi = &linehits[i]; - if (lhi->v) { - for (int j = i - 1; j >= 0; j--) { - KnifeLineHit *lhj = &linehits[j]; - if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } + if (lhi->v == NULL) { + continue; + } - if (lhi->kfe == lhj->kfe) { - lhj->l = -1.0f; - is_double = true; - } + for (int j = i - 1; j >= 0; j--) { + KnifeLineHit *lhj = &linehits[j]; + if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || + fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; } - for (int j = i + 1; j < n; j++) { - KnifeLineHit *lhj = &linehits[j]; - if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || - fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { - break; - } - if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { - lhj->l = -1.0f; - is_double = true; - } + + if (lhi->kfe == lhj->kfe) { + lhj->l = -1.0f; + is_double = true; + } + } + for (int j = i + 1; j < n; j++) { + KnifeLineHit *lhj = &linehits[j]; + if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) { + break; + } + if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) { + lhj->l = -1.0f; + is_double = true; } } } @@ -2272,11 +2269,12 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, Li /* Remove dangling edges, not essential - but nice for users. */ for (i = 0; i < edge_array_len_orig; i++) { - if (kfe_array[i]) { - if (BM_edge_is_wire(kfe_array[i]->e)) { - BM_edge_kill(bm, kfe_array[i]->e); - kfe_array[i]->e = NULL; - } + if (kfe_array[i] == NULL) { + continue; + } + if (BM_edge_is_wire(kfe_array[i]->e)) { + BM_edge_kill(bm, kfe_array[i]->e); + kfe_array[i]->e = NULL; } } @@ -2588,7 +2586,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { - float lv[3][3]; + float tri_cos[3][3]; float ray_tri_uv[2]; tri = em->looptris[tri_i]; @@ -2596,22 +2594,22 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, break; } - knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, lv); + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, tri_cos); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with * an exact test; * We will exclude hits near real edges by a later test. */ if (isect_ray_tri_epsilon_v3( - v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { + v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { /* Check if line coplanar with tri. */ - normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]); - plane_from_point_normal_v3(tri_plane, lv[0], tri_norm); + normal_tri_v3(tri_norm, UNPACK3(tri_cos)); + plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm); if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv); + interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv); /* Now check that far enough away from verts and edges. */ list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { @@ -5028,17 +5026,19 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th /* Tag all faces linked to cut edges. */ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { /* Check are we tagged?, then we are an original face. */ - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { - BMFace *f; - BMIter fiter; - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if (edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + continue; + } + + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if (edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); } } } @@ -5048,43 +5048,45 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th BMFace *f; keep_search = false; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { - /* Am I connected to a tagged face via an un-tagged edge - * (ie, not across a cut)? */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - bool found = false; - - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { - /* Now check if the adjacent faces is tagged. */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { - found = true; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && - (found == false)); - } - } - } while ((l_iter = l_iter->next) != l_first && (found == false)); - - if (found) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - mul_m4_v3(ob->obmat, cent); - knife_project_v2(kcd, cent, cent_ss); - if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && - edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - keep_search = true; - } - else { - /* Don't lose time on this face again, set it as outside. */ - F_ISECT_SET_OUTSIDE(f); + if (BM_elem_flag_test(f, BM_ELEM_TAG) || !F_ISECT_IS_UNKNOWN(f)) { + continue; + } + + /* Am I connected to a tagged face via an un-tagged edge + * (ie, not across a cut)? */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* Now check if the adjacent faces is tagged. */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && + (found == false)); } } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && + edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* Don't lose time on this face again, set it as outside. */ + F_ISECT_SET_OUTSIDE(f); + } } } } while (keep_search); diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index 3f05c27e8fa..c931cb4948b 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -1370,8 +1370,14 @@ static bool edbm_select_similar_poll_property(const bContext *UNUSED(C), const char *prop_id = RNA_property_identifier(prop); const int type = RNA_enum_get(op->ptr, "type"); + /* Only show compare when it is used. */ + if (STREQ(prop_id, "compare")) { + if (type == SIMVERT_VGROUP) { + return false; + } + } /* Only show threshold when it is used. */ - if (STREQ(prop_id, "threshold")) { + else if (STREQ(prop_id, "threshold")) { if (!ELEM(type, SIMVERT_NORMAL, SIMEDGE_BEVEL, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c3d5f33705c..c7c7e5cf2f8 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -9,6 +9,7 @@ #include "DNA_key_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "BLI_alloca.h" @@ -19,6 +20,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_global.h" diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.cc index d11f0b490c1..67834bf05ce 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.cc @@ -13,7 +13,7 @@ #include "DNA_scene_types.h" #include "DNA_view3d_types.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -43,10 +43,12 @@ #include "mesh_intern.h" /* own include */ +using blender::Array; + static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot) { CustomData *data; - BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : NULL; + BMesh *bm = (me->edit_mesh) ? me->edit_mesh->bm : nullptr; int tot; switch (htype) { @@ -93,7 +95,7 @@ static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_t default: BLI_assert(0); tot = 0; - data = NULL; + data = nullptr; break; } @@ -172,7 +174,7 @@ static void mesh_uv_reset_array(float **fuv, const int len) static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) { - float **fuv = BLI_array_alloca(fuv, f->len); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(f->len); BMIter liter; BMLoop *l; int i; @@ -181,21 +183,21 @@ static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset) fuv[i] = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; } - mesh_uv_reset_array(fuv, f->len); + mesh_uv_reset_array(fuv.data(), f->len); } static void mesh_uv_reset_mface(MPoly *mp, MLoopUV *mloopuv) { - float **fuv = BLI_array_alloca(fuv, mp->totloop); + Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(mp->totloop); for (int i = 0; i < mp->totloop; i++) { fuv[i] = mloopuv[mp->loopstart + i].uv; } - mesh_uv_reset_array(fuv, mp->totloop); + mesh_uv_reset_array(fuv.data(), mp->totloop); } -void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) +void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum) { BMEditMesh *em = me->edit_mesh; @@ -219,7 +221,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) else { /* Collect Mesh UVs */ BLI_assert(CustomData_has_layer(&me->ldata, CD_MLOOPUV)); - MLoopUV *mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum); for (int i = 0; i < me->totpoly; i++) { mesh_uv_reset_mface(&me->mpoly[i], mloopuv); @@ -229,7 +231,7 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, const int layernum) DEG_id_tag_update(&me->id, 0); } -void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me) +void ED_mesh_uv_loop_reset(bContext *C, Mesh *me) { /* could be ldata or pdata */ CustomData *ldata = GET_CD_DATA(me, ldata); @@ -239,7 +241,7 @@ void ED_mesh_uv_loop_reset(struct bContext *C, struct Mesh *me) WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } -int ED_mesh_uv_texture_add( +int ED_mesh_uv_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { /* NOTE: keep in sync with #ED_mesh_color_add. */ @@ -284,7 +286,7 @@ int ED_mesh_uv_texture_add( is_init = true; } else { - CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, name); + CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum_dst == 0) { @@ -305,7 +307,7 @@ int ED_mesh_uv_texture_add( return layernum_dst; } -void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) +void ED_mesh_uv_ensure(Mesh *me, const char *name) { BMEditMesh *em; int layernum_dst; @@ -315,25 +317,25 @@ void ED_mesh_uv_texture_ensure(struct Mesh *me, const char *name) layernum_dst = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_add(me, name, true, true, nullptr); } } else { layernum_dst = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); if (layernum_dst == 0) { - ED_mesh_uv_texture_add(me, name, true, true, NULL); + ED_mesh_uv_add(me, name, true, true, nullptr); } } } -bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) +bool ED_mesh_uv_remove_index(Mesh *me, const int n) { CustomData *ldata = GET_CD_DATA(me, ldata); CustomDataLayer *cdlu; int index; index = CustomData_get_layer_index_n(ldata, CD_MLOOPUV, n); - cdlu = (index == -1) ? NULL : &ldata->layers[index]; + cdlu = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdlu) { return false; @@ -346,24 +348,22 @@ bool ED_mesh_uv_texture_remove_index(Mesh *me, const int n) return true; } -bool ED_mesh_uv_texture_remove_active(Mesh *me) +bool ED_mesh_uv_remove_active(Mesh *me) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } -bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) +bool ED_mesh_uv_remove_named(Mesh *me, const char *name) { - /* texpoly/uv are assumed to be in sync */ CustomData *ldata = GET_CD_DATA(me, ldata); const int n = CustomData_get_named_layer(ldata, CD_MLOOPUV, name); if (n != -1) { - return ED_mesh_uv_texture_remove_index(me, n); + return ED_mesh_uv_remove_index(me, n); } return false; } @@ -371,7 +371,7 @@ bool ED_mesh_uv_texture_remove_named(Mesh *me, const char *name) int ED_mesh_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -409,7 +409,7 @@ int ED_mesh_color_add( } else { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); } if (active_set || layernum == 0) { @@ -425,14 +425,14 @@ int ED_mesh_color_add( return layernum; } -bool ED_mesh_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); if (!layer) { CustomData_add_layer_named( - &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name); layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); BKE_id_attributes_active_color_set(&me->id, layer); @@ -441,7 +441,7 @@ bool ED_mesh_color_ensure(struct Mesh *me, const char *name) DEG_id_tag_update(&me->id, 0); - return (layer != NULL); + return (layer != nullptr); } bool ED_mesh_color_remove_index(Mesh *me, const int n) @@ -451,7 +451,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n); - cdl = (index == -1) ? NULL : &ldata->layers[index]; + cdl = (index == -1) ? nullptr : &ldata->layers[index]; if (!cdl) { return false; @@ -487,7 +487,7 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name) static bool layers_poll(bContext *C) { Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; + ID *data = (ob) ? static_cast<ID *>(ob->data) : nullptr; return (ob && !ID_IS_LINKED(ob) && !ID_IS_OVERRIDE_LIBRARY(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } @@ -501,7 +501,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *vdata = GET_CD_DATA(me, vdata); const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR); if (active != -1) { @@ -514,7 +514,7 @@ static bool sculpt_vertex_color_remove_poll(bContext *C) int ED_mesh_sculpt_color_add( Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) { - /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ + /* NOTE: keep in sync with #ED_mesh_uv_add. */ BMEditMesh *em; int layernum; @@ -549,12 +549,14 @@ int ED_mesh_sculpt_color_add( } if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) { - MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *color_data = (const MPropCol *)CustomData_get_layer(&me->vdata, + CD_PROP_COLOR); CustomData_add_layer_named( - &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name); + &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, (MPropCol *)color_data, me->totvert, name); } else { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named( + &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); } if (active_set || layernum == 0) { @@ -570,18 +572,18 @@ int ED_mesh_sculpt_color_add( return layernum; } -bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name) +bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name) { - BLI_assert(me->edit_mesh == NULL); + BLI_assert(me->edit_mesh == nullptr); if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name); + CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); BKE_mesh_update_customdata_pointers(me, true); } DEG_id_tag_update(&me->id, 0); - return (me->mloopcol != NULL); + return (me->mloopcol != nullptr); } bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) @@ -591,7 +593,7 @@ bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) int index; index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n); - cdl = (index == -1) ? NULL : &vdata->layers[index]; + cdl = (index == -1) ? nullptr : &vdata->layers[index]; if (!cdl) { return false; @@ -631,7 +633,7 @@ static bool uv_texture_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV); if (active != -1) { @@ -644,16 +646,16 @@ static bool uv_texture_remove_poll(bContext *C) static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_uv_texture_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -677,16 +679,16 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot) static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (!ED_mesh_uv_texture_remove_active(me)) { + if (!ED_mesh_uv_remove_active(me)) { return OPERATOR_CANCELLED; } if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } return OPERATOR_FINISHED; @@ -716,7 +718,7 @@ static bool vertex_color_remove_poll(bContext *C) } Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); CustomData *ldata = GET_CD_DATA(me, ldata); const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); if (active != -1) { @@ -729,9 +731,9 @@ static bool vertex_color_remove_poll(bContext *C) static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -756,7 +758,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot) static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -785,9 +787,9 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot) static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); - if (ED_mesh_sculpt_color_add(me, NULL, true, true, op->reports) == -1) { + if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) { return OPERATOR_CANCELLED; } @@ -812,7 +814,7 @@ void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot) static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ED_mesh_sculpt_color_remove_active(me)) { return OPERATOR_CANCELLED; @@ -868,7 +870,7 @@ static bool mesh_customdata_mask_clear_poll(bContext *C) { Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); /* special case - can't run this if we're in sculpt mode */ if (ob->mode & OB_MODE_SCULPT) { @@ -925,7 +927,7 @@ static int mesh_customdata_skin_state(bContext *C) Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!ID_IS_LINKED(me) && !ID_IS_OVERRIDE_LIBRARY(me)) { CustomData *data = GET_CD_DATA(me, vdata); return CustomData_has_layer(data, CD_MVERT_SKIN); @@ -942,7 +944,7 @@ static bool mesh_customdata_skin_add_poll(bContext *C) static int mesh_customdata_skin_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BKE_mesh_ensure_skin_customdata(me); @@ -1025,7 +1027,7 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator me->smoothresh); } - CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, NULL, me->totloop); + CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, nullptr, me->totloop); } DEG_id_tag_update(&me->id, 0); @@ -1057,7 +1059,7 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato if (BKE_mesh_has_custom_loop_normals(me)) { BMEditMesh *em = me->edit_mesh; - if (em != NULL && em->bm->lnor_spacearr != NULL) { + if (em != nullptr && em->bm->lnor_spacearr != nullptr) { BKE_lnor_spacearr_clear(em->bm->lnor_spacearr); } return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); @@ -1114,7 +1116,7 @@ static void mesh_add_verts(Mesh *mesh, int len) CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); if (!CustomData_has_layer(&vdata, CD_MVERT)) { - CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); } CustomData_free(&mesh->vdata, mesh->totvert); @@ -1152,7 +1154,7 @@ static void mesh_add_edges(Mesh *mesh, int len) CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); if (!CustomData_has_layer(&edata, CD_MEDGE)) { - CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); + CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); } CustomData_free(&mesh->edata, mesh->totedge); @@ -1186,7 +1188,7 @@ static void mesh_add_loops(Mesh *mesh, int len) CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); if (!CustomData_has_layer(&ldata, CD_MLOOP)) { - CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); + CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); } BKE_mesh_runtime_clear_cache(mesh); @@ -1215,7 +1217,7 @@ static void mesh_add_polys(Mesh *mesh, int len) CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); if (!CustomData_has_layer(&pdata, CD_MPOLY)) { - CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); } CustomData_free(&mesh->pdata, mesh->totpoly); @@ -1413,21 +1415,21 @@ void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail) ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX); } -Mesh *ED_mesh_context(struct bContext *C) +Mesh *ED_mesh_context(bContext *C) { - Mesh *mesh = CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data; - if (mesh != NULL) { + Mesh *mesh = static_cast<Mesh *>(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data); + if (mesh != nullptr) { return mesh; } Object *ob = ED_object_active_context(C); - if (ob == NULL) { - return NULL; + if (ob == nullptr) { + return nullptr; } ID *data = (ID *)ob->data; - if (data == NULL || GS(data->name) != ID_ME) { - return NULL; + if (data == nullptr || GS(data->name) != ID_ME) { + return nullptr; } return (Mesh *)data; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 5abbfc2cb14..303234df48c 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -9,6 +9,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct BMEditMesh; struct BMElem; struct BMOperator; @@ -300,7 +304,7 @@ void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot); void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot); #endif -/* *** mesh_data.c *** */ +/* *** mesh_data.cc *** */ void MESH_OT_uv_texture_add(struct wmOperatorType *ot); void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); @@ -313,3 +317,7 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.cc index 9575fde68f0..9e28e1bafdd 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.cc @@ -21,10 +21,8 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" - #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -91,7 +89,7 @@ static void join_mesh_single(Depsgraph *depsgraph, { int a, b; - Mesh *me = ob_src->data; + Mesh *me = static_cast<Mesh *>(ob_src->data); MVert *mvert = *mvert_pp; MEdge *medge = *medge_pp; MLoop *mloop = *mloop_pp; @@ -106,12 +104,13 @@ static void join_mesh_single(Depsgraph *depsgraph, CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert); /* vertex groups */ - MDeformVert *dvert = CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); - MDeformVert *dvert_src = CustomData_get(&me->vdata, 0, CD_MDEFORMVERT); + MDeformVert *dvert = (MDeformVert *)CustomData_get(vdata, *vertofs, CD_MDEFORMVERT); + const MDeformVert *dvert_src = (const MDeformVert *)CustomData_get( + &me->vdata, 0, CD_MDEFORMVERT); /* Remap to correct new vgroup indices, if needed. */ if (dvert_src) { - BLI_assert(dvert != NULL); + BLI_assert(dvert != nullptr); /* Build src to merged mapping of vgroup indices. */ int *vgroup_index_map; @@ -120,7 +119,7 @@ static void join_mesh_single(Depsgraph *depsgraph, ob_src, ob_dst, &vgroup_index_map_len); BKE_object_defgroup_index_map_apply( dvert, me->totvert, vgroup_index_map, vgroup_index_map_len); - if (vgroup_index_map != NULL) { + if (vgroup_index_map != nullptr) { MEM_freeN(vgroup_index_map); } } @@ -150,11 +149,11 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this mesh has such a shape-key. */ - KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL; + KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key * (need to transform first) */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); mul_m4_v3(cmat, *cos); @@ -180,10 +179,10 @@ static void join_mesh_single(Depsgraph *depsgraph, float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; /* Check if this was one of the original shape-keys. */ - KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL; + KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : nullptr; if (okb) { /* copy this mesh's shape-key to the destination shape-key */ - float(*ocos)[3] = okb->data; + float(*ocos)[3] = static_cast<float(*)[3]>(okb->data); for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); } @@ -254,17 +253,17 @@ static void join_mesh_single(Depsgraph *depsgraph, } /* Face maps. */ - int *fmap = CustomData_get(pdata, *polyofs, CD_FACEMAP); - int *fmap_src = CustomData_get(&me->pdata, 0, CD_FACEMAP); + int *fmap = (int *)CustomData_get(pdata, *polyofs, CD_FACEMAP); + const int *fmap_src = (const int *)CustomData_get(&me->pdata, 0, CD_FACEMAP); /* Remap to correct new face-map indices, if needed. */ if (fmap_src) { - BLI_assert(fmap != NULL); + BLI_assert(fmap != nullptr); int *fmap_index_map; int fmap_index_map_len; fmap_index_map = BKE_object_facemap_index_map_create(ob_src, ob_dst, &fmap_index_map_len); BKE_object_facemap_index_map_apply(fmap, me->totpoly, fmap_index_map, fmap_index_map_len); - if (fmap_index_map != NULL) { + if (fmap_index_map != nullptr) { MEM_freeN(fmap_index_map); } } @@ -290,7 +289,7 @@ static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset return; } - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + int *face_sets = (int *)CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return; } @@ -317,20 +316,18 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - Material **matar = NULL, *ma; + Material **matar = nullptr, *ma; Mesh *me; - MVert *mvert = NULL; - MEdge *medge = NULL; - MPoly *mpoly = NULL; - MLoop *mloop = NULL; - Key *key, *nkey = NULL; - KeyBlock *kb, *kbn; + MVert *mvert = nullptr; + MEdge *medge = nullptr; + MPoly *mpoly = nullptr; + MLoop *mloop = nullptr; + Key *key, *nkey = nullptr; float imat[4][4]; int a, b, totcol, totmat = 0, totedge = 0, totvert = 0; - int totloop = 0, totpoly = 0, vertofs, *matmap = NULL; + int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr; int i, haskey = 0, edgeofs, loopofs, polyofs; bool ok = false, join_parent = false; - bDeformGroup *dg, *odg; CustomData vdata, edata, fdata, ldata, pdata; if (ob->mode & OB_MODE_EDIT) { @@ -349,7 +346,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* count & check */ CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == OB_MESH) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); totvert += me->totvert; totedge += me->totedge; @@ -361,7 +358,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) ok = true; } - if ((ob->parent != NULL) && (ob_iter == ob->parent)) { + if ((ob->parent != nullptr) && (ob_iter == ob->parent)) { join_parent = true; } @@ -376,7 +373,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Apply parent transform if the active object's parent was joined to it. * NOTE: This doesn't apply recursive parenting. */ if (join_parent) { - ob->parent = NULL; + ob->parent = nullptr; BKE_object_apply_mat4_ex(ob, ob->obmat, ob->parent, ob->parentinv, false); } @@ -416,12 +413,12 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* new material indices and material array */ if (totmat) { - matar = MEM_callocN(sizeof(*matar) * totmat, "join_mesh matar"); - matmap = MEM_callocN(sizeof(*matmap) * totmat, "join_mesh matmap"); + matar = static_cast<Material **>(MEM_callocN(sizeof(*matar) * totmat, __func__)); + matmap = static_cast<int *>(MEM_callocN(sizeof(*matmap) * totmat, __func__)); } totcol = ob->totcol; - /* obact materials in new main array, is nicer start! */ + /* Active object materials in new main array, is nicer start! */ for (a = 0; a < ob->totcol; a++) { matar[a] = BKE_object_material_get(ob, a + 1); id_us_plus((ID *)matar[a]); @@ -438,7 +435,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) nkey = (Key *)BKE_id_copy(bmain, &key->id); /* for all keys in old block, clear data-arrays */ - for (kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { if (kb->data) { MEM_freeN(kb->data); } @@ -462,13 +459,14 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { /* only act if a mesh, and not the one we're joining to */ if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) { - me = ob_iter->data; + me = static_cast<Mesh *>(ob_iter->data); /* Join this object's vertex groups to the base one's */ - for (dg = me->vertex_group_names.first; dg; dg = dg->next) { + LISTBASE_FOREACH (bDeformGroup *, dg, &me->vertex_group_names) { /* See if this group exists in the object (if it doesn't, add it to the end) */ if (!BKE_object_defgroup_find_name(ob, dg->name)) { - odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup"); + bDeformGroup *odg = static_cast<bDeformGroup *>( + MEM_mallocN(sizeof(bDeformGroup), __func__)); memcpy(odg, dg, sizeof(bDeformGroup)); BLI_addtail(&mesh_active->vertex_group_names, odg); } @@ -481,8 +479,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* Join this object's face maps to the base one's. */ LISTBASE_FOREACH (bFaceMap *, fmap, &ob_iter->fmaps) { /* See if this group exists in the object (if it doesn't, add it to the end) */ - if (BKE_object_facemap_find_name(ob, fmap->name) == NULL) { - bFaceMap *fmap_new = MEM_mallocN(sizeof(bFaceMap), "join faceMap"); + if (BKE_object_facemap_find_name(ob, fmap->name) == nullptr) { + bFaceMap *fmap_new = static_cast<bFaceMap *>(MEM_mallocN(sizeof(bFaceMap), __func__)); memcpy(fmap_new, fmap, sizeof(bFaceMap)); BLI_addtail(&ob->fmaps, fmap_new); } @@ -522,13 +520,15 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * check if destination mesh already has matching entries too. */ if (me->key && key) { /* for remapping KeyBlock.relative */ - int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__); - KeyBlock **kb_map = MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__); + int *index_map = static_cast<int *>( + MEM_mallocN(sizeof(int) * me->key->totkey, __func__)); + KeyBlock **kb_map = static_cast<KeyBlock **>( + MEM_mallocN(sizeof(KeyBlock *) * me->key->totkey, __func__)); - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { BLI_assert(i < me->key->totkey); - kbn = BKE_keyblock_find_name(key, kb->name); + KeyBlock *kbn = BKE_keyblock_find_name(key, kb->name); /* if key doesn't exist in destination mesh, add it */ if (kbn) { index_map[i] = BLI_findindex(&key->block, kbn); @@ -549,7 +549,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) } /* remap relative index values */ - for (kb = me->key->block.first, i = 0; kb; kb = kb->next, i++) { + LISTBASE_FOREACH_INDEX (KeyBlock *, kb, &me->key->block, i) { /* sanity check, should always be true */ if (LIKELY(kb->relative < me->key->totkey)) { kb_map[i]->relative = index_map[kb->relative]; @@ -571,10 +571,10 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CustomData_reset(&ldata); CustomData_reset(&pdata); - mvert = CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); - medge = CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); - mloop = CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); - mpoly = CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpoly); + mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert); + medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge); + mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop); + mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly); vertofs = 0; edgeofs = 0; @@ -661,7 +661,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* return to mesh we're merging to */ - me = ob->data; + me = static_cast<Mesh *>(ob->data); CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -703,8 +703,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) if (totcol) { me->mat = matar; - ob->mat = MEM_callocN(sizeof(*ob->mat) * totcol, "join obmatar"); - ob->matbits = MEM_callocN(sizeof(*ob->matbits) * totcol, "join obmatbits"); + ob->mat = static_cast<Material **>(MEM_callocN(sizeof(*ob->mat) * totcol, __func__)); + ob->matbits = static_cast<char *>(MEM_callocN(sizeof(*ob->matbits) * totcol, __func__)); MEM_freeN(matmap); } @@ -751,8 +751,8 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) Object *ob_active = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Mesh *me = (Mesh *)ob_active->data; - Mesh *selme = NULL; - Mesh *me_deformed = NULL; + Mesh *selme = nullptr; + Mesh *me_deformed = nullptr; Key *key = me->key; KeyBlock *kb; bool ok = false, nonequal_verts = false; @@ -769,7 +769,7 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) ok = true; } else { - nonequal_verts = 1; + nonequal_verts = true; } } } @@ -787,12 +787,12 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (key == NULL) { + if (key == nullptr) { key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* first key added, so it was the basis. initialize it with the existing mesh */ - kb = BKE_keyblock_add(key, NULL); + kb = BKE_keyblock_add(key, nullptr); BKE_keyblock_convert_from_mesh(me, key, kb); } @@ -835,21 +835,21 @@ int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) /** \name Mesh Topology Mirror API * \{ */ -static MirrTopoStore_t mesh_topo_store = {NULL, -1. - 1, -1}; +static MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false}; BLI_INLINE void mesh_mirror_topo_table_get_meshes(Object *ob, Mesh *me_eval, Mesh **r_me_mirror, BMEditMesh **r_em_mirror) { - Mesh *me_mirror = NULL; - BMEditMesh *em_mirror = NULL; + Mesh *me_mirror = nullptr; + BMEditMesh *em_mirror = nullptr; - Mesh *me = ob->data; - if (me_eval != NULL) { + Mesh *me = static_cast<Mesh *>(ob->data); + if (me_eval != nullptr) { me_mirror = me_eval; } - else if (me->edit_mesh != NULL) { + else if (me->edit_mesh != nullptr) { em_mirror = me->edit_mesh; } else { @@ -892,7 +892,7 @@ static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval) static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mvert = me_eval ? me_eval->mvert : me->mvert; float vec[3]; @@ -901,7 +901,7 @@ static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index) vec[1] = mvert->co[1]; vec[2] = mvert->co[2]; - return ED_mesh_mirror_spatial_table_lookup(ob, NULL, me_eval, vec); + return ED_mesh_mirror_spatial_table_lookup(ob, nullptr, me_eval, vec); } static int mesh_get_x_mirror_vert_topo(Object *ob, Mesh *mesh, int index) @@ -928,28 +928,25 @@ static BMVert *editbmesh_get_x_mirror_vert_spatial(Object *ob, BMEditMesh *em, c /* ignore nan verts */ if ((isfinite(co[0]) == false) || (isfinite(co[1]) == false) || (isfinite(co[2]) == false)) { - return NULL; + return nullptr; } vec[0] = -co[0]; vec[1] = co[1]; vec[2] = co[2]; - i = ED_mesh_mirror_spatial_table_lookup(ob, em, NULL, vec); + i = ED_mesh_mirror_spatial_table_lookup(ob, em, nullptr, vec); if (i != -1) { return BM_vert_at_index(em->bm, i); } - return NULL; + return nullptr; } -static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - int index) +static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, BMEditMesh *em, BMVert *eve, int index) { intptr_t poinval; - if (!ed_mesh_mirror_topo_table_update(ob, NULL)) { - return NULL; + if (!ed_mesh_mirror_topo_table_update(ob, nullptr)) { + return nullptr; } if (index == -1) { @@ -965,7 +962,7 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, } if (index == em->bm->totvert) { - return NULL; + return nullptr; } } @@ -974,15 +971,11 @@ static BMVert *editbmesh_get_x_mirror_vert_topo(Object *ob, if (poinval != -1) { return (BMVert *)(poinval); } - return NULL; + return nullptr; } -BMVert *editbmesh_get_x_mirror_vert(Object *ob, - struct BMEditMesh *em, - BMVert *eve, - const float co[3], - int index, - const bool use_topology) +BMVert *editbmesh_get_x_mirror_vert( + Object *ob, BMEditMesh *em, BMVert *eve, const float co[3], int index, const bool use_topology) { if (use_topology) { return editbmesh_get_x_mirror_vert_topo(ob, em, eve, index); @@ -992,7 +985,7 @@ BMVert *editbmesh_get_x_mirror_vert(Object *ob, int ED_mesh_mirror_get_vert(Object *ob, int index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; int index_mirr; @@ -1004,7 +997,7 @@ int ED_mesh_mirror_get_vert(Object *ob, int index) index_mirr = eve_mirr ? BM_elem_index_get(eve_mirr) : -1; } else { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, use_topology); + index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, use_topology); } return index_mirr; @@ -1021,7 +1014,7 @@ static float *editmesh_get_mirror_uv( /* ignore nan verts */ if (isnan(uv[0]) || !isfinite(uv[0]) || isnan(uv[1]) || !isfinite(uv[1])) { - return NULL; + return nullptr; } if (axis) { @@ -1061,14 +1054,14 @@ static float *editmesh_get_mirror_uv( } } - return NULL; + return nullptr; } #endif static uint mirror_facehash(const void *ptr) { - const MFace *mf = ptr; + const MFace *mf = static_cast<const MFace *>(ptr); uint v0, v1; if (mf->v4) { @@ -1121,21 +1114,21 @@ static bool mirror_facecmp(const void *a, const void *b) int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mv, *mvert; MFace mirrormf, *mf, *hashmf, *mface; GHash *fhash; int *mirrorverts, *mirrorfaces; - BLI_assert(em == NULL); /* Does not work otherwise, currently... */ + BLI_assert(em == nullptr); /* Does not work otherwise, currently... */ const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const int totvert = me_eval ? me_eval->totvert : me->totvert; const int totface = me_eval ? me_eval->totface : me->totface; int a; - mirrorverts = MEM_callocN(sizeof(int) * totvert, "MirrorVerts"); - mirrorfaces = MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces"); + mirrorverts = static_cast<int *>(MEM_callocN(sizeof(int) * totvert, "MirrorVerts")); + mirrorfaces = static_cast<int *>(MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces")); mvert = me_eval ? me_eval->mvert : me->mvert; mface = me_eval ? me_eval->mface : me->mface; @@ -1165,7 +1158,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) SWAP(uint, mirrormf.v2, mirrormf.v4); } - hashmf = BLI_ghash_lookup(fhash, &mirrormf); + hashmf = static_cast<MFace *>(BLI_ghash_lookup(fhash, &mirrormf)); if (hashmf) { mirrorfaces[a * 2] = hashmf - mface; mirrorfaces[a * 2 + 1] = mirror_facerotation(&mirrormf, hashmf); @@ -1175,7 +1168,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) } } - BLI_ghash_free(fhash, NULL, NULL); + BLI_ghash_free(fhash, nullptr, nullptr); MEM_freeN(mirrorverts); return mirrorfaces; @@ -1186,7 +1179,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval) bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1199,8 +1192,8 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, ED_view3d_select_id_validate(&vc); if (dist_px) { - /* sample rect to increase chances of selecting, so that when clicking - * on an edge in the backbuf, we can still select a face */ + /* Sample rect to increase chances of selecting, so that when clicking + * on an edge in the back-buffer, we can still select a face. */ *r_index = DRW_select_buffer_find_nearest_to_point( vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totpoly + 1, &dist_px); } @@ -1220,7 +1213,7 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], uint dist_px, static void ed_mesh_pick_face_vert__mpoly_find( /* context */ - struct ARegion *region, + ARegion *region, const float mval[2], /* mesh data (evaluated) */ const MPoly *mp, @@ -1250,14 +1243,14 @@ bool ED_mesh_pick_face_vert( { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); uint poly_index; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); if (ED_mesh_pick_face(C, ob, mval, dist_px, &poly_index)) { Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - struct ARegion *region = CTX_wm_region(C); + ARegion *region = CTX_wm_region(C); /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final( @@ -1266,14 +1259,13 @@ bool ED_mesh_pick_face_vert( int v_idx_best = ORIGINDEX_NONE; /* find the vert closest to 'mval' */ - const float mval_f[2] = {UNPACK2(mval)}; + const float mval_f[2] = {(float)mval[0], (float)mval[1]}; float len_best = FLT_MAX; MPoly *me_eval_mpoly; MLoop *me_eval_mloop; MVert *me_eval_mvert; uint me_eval_mpoly_len; - const int *index_mp_to_orig; me_eval_mpoly = me_eval->mpoly; me_eval_mloop = me_eval->mloop; @@ -1281,7 +1273,7 @@ bool ED_mesh_pick_face_vert( me_eval_mpoly_len = me_eval->totpoly; - index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); + const int *index_mp_to_orig = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); /* tag all verts using this face */ if (index_mp_to_orig) { @@ -1313,8 +1305,8 @@ bool ED_mesh_pick_face_vert( /* map 'dm -> me' r_index if possible */ if (v_idx_best != ORIGINDEX_NONE) { - const int *index_mv_to_orig; - index_mv_to_orig = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + const int *index_mv_to_orig = (const int *)CustomData_get_layer(&me_eval->vdata, + CD_ORIGINDEX); if (index_mv_to_orig) { v_idx_best = index_mv_to_orig[v_idx_best]; } @@ -1335,7 +1327,7 @@ bool ED_mesh_pick_face_vert( * * \return boolean true == Found */ -typedef struct VertPickData { +struct VertPickData { const MVert *mvert; const float *mval_f; /* [2] */ ARegion *region; @@ -1343,14 +1335,14 @@ typedef struct VertPickData { /* runtime */ float len_best; int v_idx_best; -} VertPickData; +}; static void ed_mesh_pick_vert__mapFunc(void *userData, int index, const float co[3], const float UNUSED(no[3])) { - VertPickData *data = userData; + VertPickData *data = static_cast<VertPickData *>(userData); if ((data->mvert[index].flag & ME_HIDE) == 0) { float sco[2]; @@ -1368,7 +1360,7 @@ bool ED_mesh_pick_vert( bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index) { ViewContext vc; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BLI_assert(me && GS(me->id.name) == ID_ME); @@ -1382,8 +1374,8 @@ bool ED_mesh_pick_vert( if (use_zbuf) { if (dist_px > 0) { - /* sample rect to increase chances of selecting, so that when clicking - * on an face in the backbuf, we can still select a vert */ + /* Sample rectangle to increase chances of selecting, so that when clicking + * on an face in the back-buffer, we can still select a vert. */ *r_index = DRW_select_buffer_find_nearest_to_point( vc.depsgraph, vc.region, vc.v3d, mval, 1, me->totvert + 1, &dist_px); } @@ -1405,16 +1397,16 @@ bool ED_mesh_pick_vert( /* derived mesh to find deformed locations */ Mesh *me_eval = mesh_get_eval_final(vc.depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH); ARegion *region = vc.region; - RegionView3D *rv3d = region->regiondata; + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); /* find the vert closest to 'mval' */ const float mval_f[2] = {(float)mval[0], (float)mval[1]}; - VertPickData data = {NULL}; + VertPickData data = {nullptr}; ED_view3d_init_mats_rv3d(ob, rv3d); - if (me_eval == NULL) { + if (me_eval == nullptr) { return false; } @@ -1440,7 +1432,7 @@ bool ED_mesh_pick_vert( MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) { if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (!BLI_listbase_is_empty(&me->vertex_group_names)) { BMesh *bm = me->edit_mesh->bm; const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); @@ -1452,27 +1444,27 @@ MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) if (r_eve) { *r_eve = eve; } - return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + return static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); } } } } if (r_eve) { - *r_eve = NULL; + *r_eve = nullptr; } - return NULL; + return nullptr; } MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); int index = BKE_mesh_mselect_active_get(me, ME_VSEL); if (r_index) { *r_index = index; } - if (index == -1 || me->dvert == NULL) { - return NULL; + if (index == -1 || me->dvert == nullptr) { + return nullptr; } return me->dvert + index; } @@ -1481,14 +1473,14 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob) { if (ob->type == OB_MESH) { if (ob->mode & OB_MODE_EDIT) { - return ED_mesh_active_dvert_get_em(ob, NULL); + return ED_mesh_active_dvert_get_em(ob, nullptr); } - return ED_mesh_active_dvert_get_ob(ob, NULL); + return ED_mesh_active_dvert_get_ob(ob, nullptr); } - return NULL; + return nullptr; } -void EDBM_mesh_stats_multi(struct Object **objects, +void EDBM_mesh_stats_multi(Object **objects, const uint objects_len, int totelem[3], int totelem_sel[3]) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 20b9844ac89..422aaa03120 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1265,9 +1265,9 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) "Relative Path", "Select the file relative to the blend file"); 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)); + + WM_operator_properties_id_lookup(ot, true); + ED_object_add_generic_props(ot, false); } @@ -1729,8 +1729,7 @@ static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent RNA_int_set(op->ptr, "drop_y", event->xy[1]); } - if (!RNA_struct_property_is_set(op->ptr, "name") && - !RNA_struct_property_is_set(op->ptr, "session_uuid")) { + if (!WM_operator_properties_id_lookup_is_set(op->ptr)) { return WM_enum_search_invoke(C, op, event); } return op->type->exec(C, op); @@ -1762,16 +1761,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) ot->prop = prop; ED_object_add_generic_props(ot, false); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); object_add_drop_xy_props(ot); } @@ -1868,20 +1858,11 @@ void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the collection to add", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + WM_operator_properties_id_lookup(ot, false); ED_object_add_generic_props(ot, false); - /* Important: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). + /* IMPORTANT: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). */ RNA_def_boolean(ot->srna, "use_instance", @@ -1913,18 +1894,12 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) ushort local_view_bits; float loc[3], rot[3]; - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); PropertyRNA *prop_type = RNA_struct_find_property(op->ptr, "type"); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); - /* These shouldn't fail when created by outliner dropping as it checks the ID is valid. */ - if (!RNA_property_is_set(op->ptr, prop_name) || !RNA_property_is_set(op->ptr, prop_type)) { - return OPERATOR_CANCELLED; - } const short id_type = RNA_property_enum_get(op->ptr, prop_type); - char name[MAX_ID_NAME - 2]; - RNA_property_string_get(op->ptr, prop_name, name); - id = BKE_libblock_find_name(bmain, id_type, name); + id = WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, (ID_Type)id_type); if (id == nullptr) { return OPERATOR_CANCELLED; } @@ -1967,7 +1942,7 @@ void OBJECT_OT_data_instance_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Name", MAX_ID_NAME - 2, "Name", "ID name to add"); + WM_operator_properties_id_lookup(ot, true); PropertyRNA *prop = RNA_def_enum(ot->srna, "type", rna_enum_id_type_items, 0, "Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); ED_object_add_generic_props(ot, false); @@ -2109,6 +2084,22 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool object_curves_empty_hair_add_poll(bContext *C) +{ + if (!U.experimental.use_new_curves_type) { + return false; + } + if (!ED_operator_objectmode(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_MESH) { + CTX_wm_operator_poll_msg_set(C, "No active mesh object"); + return false; + } + return true; +} + void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) { ot->name = "Add Empty Curves"; @@ -2116,7 +2107,7 @@ void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot) ot->idname = "OBJECT_OT_curves_empty_hair_add"; ot->exec = object_curves_empty_hair_add_exec; - ot->poll = object_curves_add_poll; + ot->poll = object_curves_empty_hair_add_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2755,9 +2746,32 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif + {OB_CURVES, "CURVES", ICON_OUTLINER_OB_CURVES, "Curves", "Curves from evaluated curve data"}, {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem *convert_target_items_fn(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *items = nullptr; + int items_num = 0; + for (const EnumPropertyItem *item = convert_target_items; item->identifier != nullptr; item++) { + if (item->value == OB_CURVES) { + if (U.experimental.use_new_curves_type) { + RNA_enum_item_add(&items, &items_num, item); + } + } + else { + RNA_enum_item_add(&items, &items_num, item); + } + } + RNA_enum_item_end(&items, &items_num); + *r_free = true; + return items; +} + static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == nullptr) { @@ -3058,6 +3072,50 @@ static int object_convert_exec(bContext *C, wmOperator *op) } ob_gpencil->actcol = actcol; } + else if (target == OB_CURVES) { + ob->flag |= OB_DONE; + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + GeometrySet geometry; + if (ob_eval->runtime.geometry_set_eval != nullptr) { + geometry = *ob_eval->runtime.geometry_set_eval; + } + + if (geometry.has_curves()) { + if (keep_original) { + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); + newob = basen->object; + + /* Decrement original curve's usage count. */ + Curve *legacy_curve = static_cast<Curve *>(newob->data); + id_us_min(&legacy_curve->id); + + /* Make a copy of the curve. */ + newob->data = BKE_id_copy(bmain, &legacy_curve->id); + } + else { + newob = ob; + } + + const CurveComponent &curve_component = *geometry.get_component_for_read<CurveComponent>(); + const Curves *curves_eval = curve_component.get_for_read(); + Curves *new_curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, newob->id.name + 2)); + + newob->data = new_curves; + newob->type = OB_CURVES; + + blender::bke::CurvesGeometry::wrap( + new_curves->geometry) = blender::bke::CurvesGeometry::wrap(curves_eval->geometry); + BKE_object_material_from_eval_data(bmain, newob, &curves_eval->id); + + BKE_object_free_derived_caches(newob); + BKE_object_free_modifiers(newob, 0); + } + else { + BKE_reportf( + op->reports, RPT_WARNING, "Object '%s' has no evaluated curves data", ob->id.name + 2); + } + } else if (ob->type == OB_MESH && target == OB_POINTCLOUD) { ob->flag |= OB_DONE; @@ -3473,6 +3531,7 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum( ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); + RNA_def_enum_funcs(ot->prop, convert_target_items_fn); RNA_def_boolean(ot->srna, "keep_original", false, diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index a7379d7e492..114b2ce8102 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -446,9 +446,9 @@ static bool bake_object_check(ViewLayer *view_layer, } if (target == R_BAKE_TARGET_VERTEX_COLORS) { - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); + const MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); if (mloopcol == NULL && !mcol_valid) { BKE_reportf(reports, RPT_ERROR, @@ -943,9 +943,9 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re } Mesh *me = ob->data; - MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); + const MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); if (mloopcol == NULL && !mcol_valid) { BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to"); return false; diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 2e878770347..d982d86fe77 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1715,7 +1715,7 @@ static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* Copy all constraints from active posebone to all selected posebones. */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* If we're not handling the object we're copying from, copy all constraints over. */ if (pchan == chan) { @@ -2115,7 +2115,7 @@ static int pose_constraint_copy_exec(bContext *C, wmOperator *op) Object *prev_ob = NULL; - /* copy all constraints from active posebone to all selected posebones */ + /* Copy all constraints from active pose-bone to all selected pose-bones. */ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { /* if we're not handling the object we're copying from, copy all constraints over */ if (pchan != chan) { diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index dfe858e5bd9..b9a4c79ac03 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -45,7 +45,7 @@ * Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */ /* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */ static const EnumPropertyItem DT_layer_items[] = { - {0, "", 0, "Vertex Data", ""}, + RNA_ENUM_ITEM_HEADING("Vertex Data", NULL), {DT_TYPE_MDEFORMVERT, "VGROUP_WEIGHTS", 0, @@ -60,7 +60,8 @@ static const EnumPropertyItem DT_layer_items[] = { {DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"}, #endif {DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"}, - {0, "", 0, "Edge Data", ""}, + + RNA_ENUM_ITEM_HEADING("Edge Data", NULL), {DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"}, {DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"}, {DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"}, @@ -70,11 +71,13 @@ static const EnumPropertyItem DT_layer_items[] = { 0, "Freestyle Mark", "Transfer Freestyle edge mark"}, - {0, "", 0, "Face Corner Data", ""}, + + RNA_ENUM_ITEM_HEADING("Face Corner Data", NULL), {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, {DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, "VCOL", 0, "Colors", "Color Attributes"}, {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, - {0, "", 0, "Face Data", ""}, + + RNA_ENUM_ITEM_HEADING("Face Data", NULL), {DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"}, {DT_TYPE_FREESTYLE_FACE, "FREESTYLE_FACE", diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index c0637f4ac04..e5dd9fb2c8b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -17,6 +17,7 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -86,6 +87,7 @@ #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "RNA_types.h" #include "UI_interface_icons.h" @@ -1467,6 +1469,8 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1514,6 +1518,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } @@ -1583,6 +1588,25 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_boolean( + ot->srna, + "use_auto_smooth", + false, + "Auto Smooth", + "Enable automatic smooth based on smooth/sharp faces/edges and angle between faces"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_property(ot->srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(30.0f)); + RNA_def_property_ui_text(prop, + "Angle", + "Maximum angle between face normals that will be considered as smooth" + "(unused if custom split normals data are available)"); } /** \} */ diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index cc6aa34d39d..fdf2cae026d 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -143,7 +143,12 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } break; case OB_CURVES: - if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) { + if (U.experimental.use_new_curves_tools) { + if (mode & OB_MODE_EDIT) { + return true; + } + } + if (mode & OB_MODE_SCULPT_CURVES) { return true; } break; diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 175d5039e9c..202c6d96a47 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -515,12 +515,12 @@ void ED_object_modifier_copy_to_object(bContext *C, DEG_relations_tag_update(bmain); } -bool ED_object_modifier_convert(ReportList *UNUSED(reports), - Main *bmain, - Depsgraph *depsgraph, - ViewLayer *view_layer, - Object *ob, - ModifierData *md) +bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports), + Main *bmain, + Depsgraph *depsgraph, + ViewLayer *view_layer, + Object *ob, + ModifierData *md) { int cvert = 0; @@ -1608,7 +1608,7 @@ void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ -/** \name Convert Modifier Operator +/** \name Convert Particle System Modifier to Mesh Operator * \{ */ static int modifier_convert_exec(bContext *C, wmOperator *op) @@ -1618,18 +1618,12 @@ static int modifier_convert_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata"); - if (!md || !ED_object_modifier_convert(op->reports, bmain, depsgraph, view_layer, ob, md)) { + if (!md || !ED_object_modifier_convert_psys_to_mesh( + op->reports, bmain, depsgraph, view_layer, ob, md)) { return OPERATOR_CANCELLED; } - if (do_merge_customdata && - (mti->type & (eModifierTypeType_Constructive | eModifierTypeType_Nonconstructive))) { - BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data); - } - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1646,7 +1640,7 @@ static int modifier_convert_invoke(bContext *C, wmOperator *op, const wmEvent *U void OBJECT_OT_modifier_convert(wmOperatorType *ot) { - ot->name = "Convert Modifier"; + ot->name = "Convert Particles to Mesh"; ot->description = "Convert particles to a mesh object"; ot->idname = "OBJECT_OT_modifier_convert"; @@ -1657,13 +1651,6 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); - - RNA_def_boolean( - ot->srna, - "merge_customdata", - true, - "Merge UV's", - "Merge UV coordinates that share a vertex to account for imprecision in some modifiers"); } /** \} */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 111f3b6bf92..6cfd322b2e2 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1653,7 +1653,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) "Link Instance Collection", "Replace assigned Collection Instance"}, {MAKE_LINKS_FONTS, "FONTS", 0, "Link Fonts to Text", "Replace Text object Fonts"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Copy Modifiers", "Replace Modifiers"}, {MAKE_LINKS_SHADERFX, "EFFECTS", @@ -2258,49 +2258,6 @@ static bool make_override_library_object_overridable_check(Main *bmain, Object * return false; } -/* Set the object to override. */ -static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *obact = ED_object_active_context(C); - - /* Sanity checks. */ - if (!scene || ID_IS_LINKED(scene) || !obact) { - return OPERATOR_CANCELLED; - } - - if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && - ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || - make_override_library_object_overridable_check(bmain, obact)) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); - uiLayout *layout = UI_popup_menu_layout(pup); - - /* Create operator menu item with relevant properties filled in. */ - PointerRNA opptr_dummy; - uiItemFullO_ptr( - layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy); - - /* Present the menu and be done... */ - UI_popup_menu_end(C, pup); - - /* This invoke just calls another instance of this operator... */ - return OPERATOR_INTERFACE; - } - - if (ID_IS_LINKED(obact)) { - /* Show menu with list of directly linked collections containing the active object. */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - - /* Error.. cannot continue. */ - BKE_report(op->reports, - RPT_ERROR, - "Can only make library override for a referenced object or collection"); - return OPERATOR_CANCELLED; -} - static int make_override_library_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -2310,8 +2267,12 @@ static int make_override_library_exec(bContext *C, wmOperator *op) ID *id_root = NULL; bool is_override_instancing_object = false; - GSet *user_overrides_objects_uids = BLI_gset_new( - BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable"); + + GSet *user_overrides_objects_uids = do_fully_editable ? NULL : + BLI_gset_new(BLI_ghashutil_inthash_p, + BLI_ghashutil_intcmp, + __func__); bool user_overrides_from_selected_objects = false; if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && @@ -2359,7 +2320,10 @@ static int make_override_library_exec(bContext *C, wmOperator *op) user_overrides_from_selected_objects = true; } - if (user_overrides_from_selected_objects) { + if (do_fully_editable) { + /* Pass. */ + } + else if (user_overrides_from_selected_objects) { /* Only selected objects can be 'user overrides'. */ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) { BLI_gset_add(user_overrides_objects_uids, POINTER_FROM_UINT(ob_iter->id.session_uuid)); @@ -2379,25 +2343,34 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); ID *id_root_override; - const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, &id_root_override); - - /* Define liboverrides from selected/validated objects as user defined. */ - ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; - ID *id_iter; - FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || - id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { - continue; - } - if (BLI_gset_haskey(user_overrides_objects_uids, - POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { - id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + const bool success = BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id_root, + id_root, + &obact->id, + &id_root_override, + do_fully_editable); + + if (!do_fully_editable) { + /* Define liboverrides from selected/validated objects as user defined. */ + ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root; + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) || + id_iter->override_library->hierarchy_root != id_hierarchy_root_override) { + continue; + } + if (BLI_gset_haskey(user_overrides_objects_uids, + POINTER_FROM_UINT(id_iter->override_library->reference->session_uuid))) { + id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + } } - } - FOREACH_MAIN_ID_END; + FOREACH_MAIN_ID_END; - BLI_gset_free(user_overrides_objects_uids, NULL); + BLI_gset_free(user_overrides_objects_uids, NULL); + } /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ @@ -2411,6 +2384,37 @@ static int make_override_library_exec(bContext *C, wmOperator *op) return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +/* Set the object to override. */ +static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *obact = ED_object_active_context(C); + + /* Sanity checks. */ + if (!scene || ID_IS_LINKED(scene) || !obact) { + return OPERATOR_CANCELLED; + } + + if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || + make_override_library_object_overridable_check(bmain, obact)) { + return make_override_library_exec(C, op); + } + + if (ID_IS_LINKED(obact)) { + /* Show menu with list of directly linked collections containing the active object. */ + WM_enum_search_invoke(C, op, event); + return OPERATOR_CANCELLED; + } + + /* Error.. cannot continue. */ + BKE_report(op->reports, + RPT_ERROR, + "Can only make library override for a referenced object or collection"); + return OPERATOR_CANCELLED; +} + static bool make_override_library_poll(bContext *C) { Object *obact = CTX_data_active_object(C); @@ -2480,6 +2484,13 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; + + prop = RNA_def_boolean(ot->srna, + "do_fully_editable", + false, + "Create Fully Editable", + "Make all created override data-blocks fully editable"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index a56f513e98f..addcedc4481 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -1306,7 +1306,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) { static const int flags = IB_rect | IB_multilayer | IB_metadata; - char path[FILE_MAX]; + char filepath[FILE_MAX]; const char *folder; if (!(brush->icon_imbuf)) { @@ -1315,22 +1315,22 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) if (brush->icon_filepath[0]) { /* First use the path directly to try and load the file. */ - BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); - BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); + BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); /* Use default color-spaces for brushes. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); /* otherwise lets try to find it in other directories */ if (!(brush->icon_imbuf)) { folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons"); BLI_make_file_string( - BKE_main_blendfile_path_from_global(), path, folder, brush->icon_filepath); + BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath); - if (path[0]) { + if (filepath[0]) { /* Use default color spaces. */ - brush->icon_imbuf = IMB_loadiffname(path, flags, nullptr); + brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr); } } @@ -1821,13 +1821,13 @@ void PreviewLoadJob::run_fn(void *customdata, 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]; + const char *filepath = &deferred_data[1]; - // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path); + // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, filepath); - IMB_thumb_path_lock(path); - ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source); - IMB_thumb_path_unlock(path); + IMB_thumb_path_lock(filepath); + ImBuf *thumb = IMB_thumb_manage(filepath, THB_LARGE, source); + IMB_thumb_path_unlock(filepath); if (thumb) { /* PreviewImage assumes premultiplied alpha... */ diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index a13177942a8..57a9e6be917 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -67,7 +67,10 @@ static Scene *scene_add(Main *bmain, Scene *scene_old, eSceneCopyMethod method) } /** Add a new scene in the sequence editor. */ -static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod method) +Scene *ED_scene_sequencer_add(Main *bmain, + bContext *C, + eSceneCopyMethod method, + const bool assign_strip) { Sequence *seq = NULL; Scene *scene_active = CTX_data_scene(C); @@ -88,6 +91,11 @@ static Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod Scene *scene_new = scene_add(bmain, scene_strip, method); + /* If don't need assign the scene to the strip, nothing else to do. */ + if (!assign_strip) { + return scene_new; + } + /* As the scene is created in sequencer, do not set the new scene as active. * This is useful for story-boarding where we want to keep actual scene active. * The new scene is linked to the active strip and the viewport updated. */ @@ -291,7 +299,7 @@ static int scene_new_sequencer_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); int type = RNA_enum_get(op->ptr, "type"); - if (ED_scene_sequencer_add(bmain, C, type) == NULL) { + if (ED_scene_sequencer_add(bmain, C, type, true) == NULL) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index d5385181055..43c19670a41 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1315,7 +1315,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *area) else { ED_screen_state_toggle(C, win, area, state); } - /* warning: 'area' may be freed */ + /* WARNING: 'area' may be freed */ } /* otherwise just tile the area again */ else { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index d7cf09ca89a..a922e5aaaee 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -685,10 +685,10 @@ bool ED_operator_mask(bContext *C) return false; } -bool ED_operator_camera(bContext *C) +bool ED_operator_camera_poll(bContext *C) { struct Camera *cam = CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data; - return (cam != NULL); + return (cam != NULL && !ID_IS_LINKED(cam)); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 08eed52f440..dfd8f8b1bb9 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -27,12 +27,14 @@ set(INC ) set(SRC - curves_sculpt_3d_brush.cc curves_sculpt_add.cc + curves_sculpt_brush.cc curves_sculpt_comb.cc curves_sculpt_delete.cc curves_sculpt_grow_shrink.cc curves_sculpt_ops.cc + curves_sculpt_selection.cc + curves_sculpt_selection_paint.cc curves_sculpt_snake_hook.cc paint_canvas.cc paint_cursor.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 0d399419ad8..fea0e862707 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -18,9 +18,11 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_spline.hh" #include "DNA_brush_enums.h" @@ -35,6 +37,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -62,7 +66,7 @@ class AddOperation : public CurvesSculptStrokeOperation { } } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; static void initialize_straight_curve_positions(const float3 &p1, @@ -81,11 +85,12 @@ static void initialize_straight_curve_positions(const float3 &p1, */ struct AddOperationExecutor { AddOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; + const View3D *v3d_ = nullptr; + + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; @@ -94,9 +99,9 @@ struct AddOperationExecutor { Span<MLoopTri> surface_looptris_; Span<float3> corner_normals_su_; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - BrushCurvesSculptSettings *brush_settings_ = nullptr; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + const BrushCurvesSculptSettings *brush_settings_ = nullptr; float brush_radius_re_; float2 brush_pos_re_; @@ -104,10 +109,11 @@ struct AddOperationExecutor { bool use_front_face_; bool interpolate_length_; bool interpolate_shape_; + bool interpolate_point_count_; bool use_interpolation_; float new_curve_length_; int add_amount_; - int points_per_curve_ = 8; + int constant_points_per_curve_; /** Various matrices to convert between coordinate spaces. */ float4x4 curves_to_world_mat_; @@ -128,14 +134,23 @@ struct AddOperationExecutor { Vector<int> looptri_indices; }; - void execute(AddOperation &self, bContext *C, const StrokeExtension &stroke_extension) + struct NeighborInfo { + /* Curve index of the neighbor. */ + int index; + /* The weights of all neighbors of a new curve add up to 1. */ + float weight; + }; + static constexpr int max_neighbors = 5; + using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + + void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -162,18 +177,21 @@ struct AddOperationExecutor { surface_->totloop}; curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); brush_settings_ = brush_->curves_sculpt_settings; - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_re_ = brush_radius_get(*scene_, *brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; use_front_face_ = brush_->flag & BRUSH_FRONTFACE; const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); add_amount_ = std::max(0, brush_settings_->add_amount); + constant_points_per_curve_ = std::max(2, brush_settings_->points_per_curve); interpolate_length_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; interpolate_shape_ = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; - use_interpolation_ = interpolate_length_ || interpolate_shape_; + interpolate_point_count_ = brush_settings_->flag & + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; + use_interpolation_ = interpolate_length_ || interpolate_shape_ || interpolate_point_count_; new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); @@ -183,7 +201,9 @@ struct AddOperationExecutor { return; } - RandomNumberGenerator rng{(uint32_t)(PIL_check_seconds_timer() * 1000000.0f)}; + const double time = PIL_check_seconds_timer() * 1000000.0; + /* Use a pointer cast to avoid overflow warnings. */ + RandomNumberGenerator rng{*(uint32_t *)(&time)}; BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); @@ -194,13 +214,13 @@ struct AddOperationExecutor { /* Sample points on the surface using one of multiple strategies. */ AddedPoints added_points; if (add_amount_ == 1) { - this->sample_in_center(added_points); + this->sample_in_center_with_symmetry(added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected(rng, added_points); + this->sample_projected_with_symmetry(rng, added_points); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical(rng, added_points); + this->sample_spherical_with_symmetry(rng, added_points); } else { BLI_assert_unreachable(); @@ -211,20 +231,31 @@ struct AddOperationExecutor { return; } + Array<NeighborsVector> neighbors_per_curve; if (use_interpolation_) { this->ensure_curve_roots_kdtree(); + neighbors_per_curve = this->find_curve_neighbors(added_points); } + /* Resize to add the new curves, building the offsets in the array owned by the curves. */ const int tot_added_curves = added_points.bary_coords.size(); - const int tot_added_points = tot_added_curves * points_per_curve_; + curves_->resize(curves_->points_num(), curves_->curves_num() + tot_added_curves); + if (interpolate_point_count_) { + this->initialize_curve_offsets_with_interpolation(neighbors_per_curve); + } + else { + this->initialize_curve_offsets_without_interpolation(constant_points_per_curve_); + } - curves_->resize(curves_->points_num() + tot_added_points, - curves_->curves_num() + tot_added_curves); + /* Resize to add the correct point count calculated as part of building the offsets. */ + curves_->resize(curves_->offsets().last(), curves_->curves_num()); - threading::parallel_invoke([&]() { this->initialize_curve_offsets(tot_added_curves); }, - [&]() { this->initialize_attributes(added_points); }); + this->initialize_attributes(added_points, neighbors_per_curve); + + curves_->update_curve_types(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } @@ -241,13 +272,27 @@ struct AddOperationExecutor { /** * Sample a single point exactly at the mouse position. */ - void sample_in_center(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(AddedPoints &r_added_points) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, brush_pos_re_, ray_start_wo, ray_end_wo, true); const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_in_center( + r_added_points, brush_transform * ray_start_su, brush_transform * ray_end_su); + } + } + + void sample_in_center(AddedPoints &r_added_points, + const float3 &ray_start_su, + const float3 &ray_end_su) + { const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -280,11 +325,23 @@ struct AddOperationExecutor { /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_projected(rng, r_added_points, brush_transform); + } + } + + void sample_projected(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float4x4 &brush_transform) { + const int old_amount = r_added_points.bary_coords.size(); const int max_iterations = std::max(100'000, add_amount_ * 10); int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -296,8 +353,8 @@ struct AddOperationExecutor { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( depsgraph_, region_, v3d_, pos_re, ray_start_wo, ray_end_wo, true); - const float3 ray_start_su = world_to_surface_mat_ * ray_start_wo; - const float3 ray_end_su = world_to_surface_mat_ * ray_end_wo; + const float3 ray_start_su = brush_transform * (world_to_surface_mat_ * ray_start_wo); + const float3 ray_end_su = brush_transform * (world_to_surface_mat_ * ray_end_wo); const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; @@ -339,7 +396,7 @@ struct AddOperationExecutor { /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) { /* Find ray that starts in the center of the brush. */ float3 brush_ray_start_wo, brush_ray_end_wo; @@ -347,7 +404,6 @@ struct AddOperationExecutor { depsgraph_, region_, v3d_, brush_pos_re_, brush_ray_start_wo, brush_ray_end_wo, true); const float3 brush_ray_start_su = world_to_surface_mat_ * brush_ray_start_wo; const float3 brush_ray_end_su = world_to_surface_mat_ * brush_ray_end_wo; - const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); /* Find ray that starts on the boundary of the brush. That is used to compute the brush radius * in 3D. */ @@ -362,6 +418,27 @@ struct AddOperationExecutor { const float3 brush_radius_ray_start_su = world_to_surface_mat_ * brush_radius_ray_start_wo; const float3 brush_radius_ray_end_su = world_to_surface_mat_ * brush_radius_ray_end_wo; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->sample_spherical(rng, + r_added_points, + brush_transform * brush_ray_start_su, + brush_transform * brush_ray_end_su, + brush_transform * brush_radius_ray_start_su, + brush_transform * brush_radius_ray_end_su); + } + } + + void sample_spherical(RandomNumberGenerator &rng, + AddedPoints &r_added_points, + const float3 &brush_ray_start_su, + const float3 &brush_ray_end_su, + const float3 &brush_radius_ray_start_su, + const float3 &brush_radius_ray_end_su) + { + const float3 brush_ray_direction_su = math::normalize(brush_ray_end_su - brush_ray_start_su); + BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; @@ -426,7 +503,8 @@ struct AddOperationExecutor { const int max_iterations = 5; int current_iteration = 0; - while (r_added_points.bary_coords.size() < add_amount_) { + const int old_amount = r_added_points.bary_coords.size(); + while (r_added_points.bary_coords.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } @@ -506,8 +584,8 @@ struct AddOperationExecutor { } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > add_amount_) { - const int index_to_remove = rng.get_int32(r_added_points.bary_coords.size()); + while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + const int index_to_remove = rng.get_int32(add_amount_) + old_amount; r_added_points.bary_coords.remove_and_reorder(index_to_remove); r_added_points.looptri_indices.remove_and_reorder(index_to_remove); r_added_points.positions_cu.remove_and_reorder(index_to_remove); @@ -527,33 +605,42 @@ struct AddOperationExecutor { } } - void initialize_curve_offsets(const int tot_added_curves) + void initialize_curve_offsets_with_interpolation(const Span<NeighborsVector> neighbors_per_curve) { - MutableSpan<int> offsets = curves_->offsets_for_write(); - threading::parallel_for(IndexRange(tot_added_curves), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int curve_i = tot_old_curves_ + i; - offsets[curve_i + 1] = tot_old_points_ + (i + 1) * points_per_curve_; + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + + attribute_math::DefaultMixer<int> mixer{new_offsets}; + threading::parallel_for(neighbors_per_curve.index_range(), 1024, [&](IndexRange curves_range) { + for (const int i : curves_range) { + if (neighbors_per_curve[i].is_empty()) { + mixer.mix_in(i, constant_points_per_curve_, 1.0f); + } + else { + for (const NeighborInfo &neighbor : neighbors_per_curve[i]) { + const int neighbor_points_num = curves_->points_for_curve(neighbor.index).size(); + mixer.mix_in(i, neighbor_points_num, neighbor.weight); + } + } } }); - } + mixer.finalize(); - struct NeighborInfo { - /* Curve index of the neighbor. */ - int index; - /* The weights of all neighbors of a new curve add up to 1. */ - float weight; - }; - static constexpr int max_neighbors = 5; - using NeighborsVector = Vector<NeighborInfo, max_neighbors>; + bke::curves::accumulate_counts_to_offsets(new_offsets, tot_old_points_); + } - void initialize_attributes(const AddedPoints &added_points) + void initialize_curve_offsets_without_interpolation(const int points_per_curve) { - Array<NeighborsVector> neighbors_per_curve; - if (use_interpolation_) { - neighbors_per_curve = this->find_curve_neighbors(added_points); + MutableSpan<int> new_offsets = curves_->offsets_for_write().drop_front(tot_old_curves_); + int offset = tot_old_points_; + for (const int i : new_offsets.index_range()) { + new_offsets[i] = offset; + offset += points_per_curve; } + } + void initialize_attributes(const AddedPoints &added_points, + const Span<NeighborsVector> neighbors_per_curve) + { Array<float> new_lengths_cu(added_points.bary_coords.size()); if (interpolate_length_) { this->interpolate_lengths(neighbors_per_curve, new_lengths_cu); @@ -565,6 +652,8 @@ struct AddOperationExecutor { Array<float3> new_normals_su = this->compute_normals_for_added_curves_su(added_points); this->initialize_surface_attachment(added_points); + this->fill_new_selection(); + if (interpolate_shape_) { this->initialize_position_with_interpolation( added_points, neighbors_per_curve, new_normals_su, new_lengths_cu); @@ -575,6 +664,33 @@ struct AddOperationExecutor { } } + /** + * Select newly created points or curves in new curves if necessary. + */ + void fill_new_selection() + { + switch (curves_id_->selection_domain) { + case ATTR_DOMAIN_CURVE: { + const VArray<float> selection = curves_->selection_curve_float(); + if (selection.is_single() && selection.get_internal_single() >= 1.0f) { + return; + } + curves_->selection_curve_float_for_write().drop_front(tot_old_curves_).fill(1.0f); + break; + } + case ATTR_DOMAIN_POINT: { + const VArray<float> selection = curves_->selection_point_float(); + if (selection.is_single() && selection.get_internal_single() >= 1.0f) { + return; + } + curves_->selection_point_float_for_write().drop_front(tot_old_points_).fill(1.0f); + break; + } + default: + BLI_assert_unreachable(); + } + } + Array<NeighborsVector> find_curve_neighbors(const AddedPoints &added_points) { const int tot_added_curves = added_points.bary_coords.size(); @@ -613,10 +729,9 @@ struct AddOperationExecutor { for (const NeighborInfo &neighbor : neighbors) { const IndexRange neighbor_points = curves_->points_for_curve(neighbor.index); float neighbor_length = 0.0f; - const int tot_segments = neighbor_points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1 = positions_cu[neighbor_points[segment_i]]; - const float3 &p2 = positions_cu[neighbor_points[segment_i] + 1]; + for (const int segment_i : neighbor_points.drop_back(1)) { + const float3 &p1 = positions_cu[segment_i]; + const float3 &p2 = positions_cu[segment_i + 1]; neighbor_length += math::distance(p1, p2); } length_sum += neighbor.weight * neighbor_length; @@ -682,15 +797,14 @@ struct AddOperationExecutor { threading::parallel_for( added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { - const int first_point_i = tot_old_points_ + i * points_per_curve_; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float3 &root_cu = added_points.positions_cu[i]; const float length = lengths_cu[i]; const float3 &normal_su = normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 tip_cu = root_cu + length * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); } }); } @@ -711,23 +825,22 @@ struct AddOperationExecutor { added_points.bary_coords.index_range(), 256, [&](const IndexRange range) { for (const int i : range) { const Span<NeighborInfo> neighbors = neighbors_per_curve[i]; + const IndexRange points = curves_->points_for_curve(tot_old_curves_ + i); const float length_cu = new_lengths_cu[i]; const float3 &normal_su = new_normals_su[i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat_ * normal_su); const float3 &root_cu = added_points.positions_cu[i]; - const int first_point_i = tot_old_points_ + i * points_per_curve_; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ const float3 tip_cu = root_cu + length_cu * normal_cu; - initialize_straight_curve_positions( - root_cu, tip_cu, positions_cu.slice(first_point_i, points_per_curve_)); + initialize_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points)); continue; } - positions_cu.slice(first_point_i, points_per_curve_).fill(root_cu); + positions_cu.slice(points).fill(root_cu); for (const NeighborInfo &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; @@ -761,8 +874,8 @@ struct AddOperationExecutor { const float neighbor_length_cu = neighbor_spline.length(); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points_per_curve_ - 1.0f)) * length_factor; - for (const int j : IndexRange(points_per_curve_)) { + const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; + for (const int j : IndexRange(points.size())) { const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( j * resample_factor); const float index_factor = lookup.evaluated_index + lookup.factor; @@ -772,7 +885,7 @@ struct AddOperationExecutor { const float3 relative_coord = p - neighbor_root_cu; float3 rotated_relative_coord = relative_coord; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[first_point_i + j] += neighbor.weight * rotated_relative_coord; + positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; } } } @@ -780,14 +893,23 @@ struct AddOperationExecutor { } }; -void AddOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { AddOperationExecutor executor; executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports) { + const Object &ob_active = *CTX_data_active_object(&C); + BLI_assert(ob_active.type == OB_CURVES); + const Curves &curves_id = *static_cast<Curves *>(ob_active.data); + if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { + BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); + return {}; + } + return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index 945bb09c0c6..92ce6ba3153 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_3d_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -41,9 +41,9 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float3 &ray_start_cu, const float3 &ray_end_cu, const float brush_radius_re, - ARegion ®ion, - RegionView3D &rv3d, - Object &object) + const ARegion ®ion, + const RegionView3D &rv3d, + const Object &object) { /* This value might have to be adjusted based on user feedback. */ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); @@ -94,11 +94,30 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu for (const int curve_i : curves_range) { const IndexRange points = curves.points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions[points[segment_i]]; - const float3 &p2_cu = positions[points[segment_i] + 1]; + if (points.size() == 1) { + const float3 &pos_cu = positions[points.first()]; + + const float depth_sq_cu = math::distance_squared(ray_start_cu, pos_cu); + if (depth_sq_cu > max_depth_sq_cu) { + continue; + } + + float2 pos_re; + ED_view3d_project_float_v2_m4(®ion, pos_cu, pos_re, projection.values); + + BrushPositionCandidate candidate; + candidate.position_cu = pos_cu; + candidate.depth_sq_cu = depth_sq_cu; + candidate.distance_sq_re = math::distance_squared(brush_pos_re, pos_re); + + update_if_better(best_candidate, candidate); + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions[segment_i]; + const float3 &p2_cu = positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(®ion, p1_cu, p1_re, projection.values); @@ -116,8 +135,11 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float distance_sq_re = math::distance_squared(brush_pos_re, closest_re); + float3 brush_position_cu; + closest_to_line_segment_v3(brush_position_cu, closest_cu, ray_start_cu, ray_end_cu); + BrushPositionCandidate candidate; - candidate.position_cu = closest_cu; + candidate.position_cu = brush_position_cu; candidate.depth_sq_cu = depth_sq_cu; candidate.distance_sq_re = distance_sq_re; @@ -138,23 +160,21 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu return best_candidate.position_cu; } -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, const float brush_radius_re) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C); - ARegion *region = CTX_wm_region(&C); - View3D *v3d = CTX_wm_view3d(&C); - RegionView3D *rv3d = CTX_wm_region_view3d(&C); - - Curves &curves_id = *static_cast<Curves *>(curves_object.data); - CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - Object *surface_object = curves_id.surface; + const Curves &curves_id = *static_cast<Curves *>(curves_object.data); + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + const Object *surface_object = curves_id.surface; float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( - depsgraph, region, v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); + &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ if (surface_object != nullptr) { @@ -202,8 +222,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, center_ray_start_cu, center_ray_end_cu, brush_radius_re, - *region, - *rv3d, + region, + rv3d, curves_object); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ @@ -213,9 +233,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, /* Determine the 3D brush radius. */ float3 radius_ray_start_wo, radius_ray_end_wo; - ED_view3d_win_to_segment_clipped(depsgraph, - region, - v3d, + ED_view3d_win_to_segment_clipped(&depsgraph, + ®ion, + &v3d, brush_pos_re + float2(brush_radius_re, 0.0f), radius_ray_start_wo, radius_ray_end_wo, @@ -229,4 +249,32 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, return brush_3d; } +Vector<float4x4> get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry) +{ + Vector<float4x4> matrices; + + auto symmetry_to_factors = [&](const eCurvesSymmetryType type) -> Span<float> { + if (symmetry & type) { + static std::array<float, 2> values = {1.0f, -1.0f}; + return values; + } + static std::array<float, 1> values = {1.0f}; + return values; + }; + + for (const float x : symmetry_to_factors(CURVES_SYMMETRY_X)) { + for (const float y : symmetry_to_factors(CURVES_SYMMETRY_Y)) { + for (const float z : symmetry_to_factors(CURVES_SYMMETRY_Z)) { + float4x4 matrix = float4x4::identity(); + matrix.values[0][0] = x; + matrix.values[1][1] = y; + matrix.values[2][2] = z; + matrices.append(matrix); + } + } + } + + return matrices; +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 232d632aa3f..46332a66f60 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -37,6 +37,8 @@ #include "UI_interface.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -67,7 +69,7 @@ class CombOperation : public CurvesSculptStrokeOperation { friend struct CombOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -76,24 +78,28 @@ class CombOperation : public CurvesSculptStrokeOperation { */ struct CombOperationExecutor { CombOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; + VArray<float> point_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + const Object *surface_ob_ = nullptr; const Mesh *surface_ = nullptr; Span<MLoopTri> surface_looptris_; @@ -110,24 +116,24 @@ struct CombOperationExecutor { BVHTreeFromMesh surface_bvh_; - void execute(CombOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(CombOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); curves_to_world_mat_ = object_->obmat; world_to_curves_mat_ = curves_to_world_mat_.inverted(); @@ -140,6 +146,9 @@ struct CombOperationExecutor { return; } + point_factors_ = get_point_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; @@ -173,10 +182,10 @@ struct CombOperationExecutor { EnumerableThreadSpecific<Vector<int>> changed_curves; if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->comb_projected(changed_curves); + this->comb_projected_with_symmetry(changed_curves); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->comb_spherical(changed_curves); + this->comb_spherical_with_symmetry(changed_curves); } else { BLI_assert_unreachable(); @@ -186,28 +195,42 @@ struct CombOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } /** * Do combing in screen space. */ - void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_projected(r_changed_curves, brush_transform); + } + } + + void comb_projected(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); - const float brush_radius_sq_re = pow2f(brush_radius_re_); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); - threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); - for (const int curve_i : curves_range) { + for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = positions_cu[point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; /* Find the position of the point in screen space. */ float2 old_pos_re; @@ -223,16 +246,18 @@ struct CombOperationExecutor { const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); /* A falloff that is based on how far away the point is from the stroke. */ const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); + brush_, distance_to_brush_re, brush_radius_re); /* Combine the falloff and brush strength. */ - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; - /* Offset the old point position in screen space and transform it back into 3D space. */ + /* Offset the old point position in screen space and transform it back into 3D space. + */ const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * + (world_to_curves_mat_ * new_position_wo); positions_cu[point_i] = new_position_cu; curve_changed = true; @@ -247,10 +272,8 @@ struct CombOperationExecutor { /** * Do combing in 3D space. */ - void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) + void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -268,14 +291,30 @@ struct CombOperationExecutor { const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->comb_spherical(r_changed_curves, + brush_transform * brush_start_cu, + brush_transform * brush_end_cu, + brush_radius_cu); + } + } + + void comb_spherical(EnumerableThreadSpecific<Vector<int>> &r_changed_curves, + const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; - threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); - for (const int curve_i : curves_range) { + for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { @@ -295,7 +334,7 @@ struct CombOperationExecutor { const float radius_falloff = BKE_brush_curve_strength( brush_, distance_to_brush_cu, brush_radius_cu); /* Combine the falloff and brush strength. */ - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; /* Update the point position. */ positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; @@ -314,7 +353,7 @@ struct CombOperationExecutor { void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -353,11 +392,11 @@ struct CombOperationExecutor { threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : changed_curves.as_span().slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - float3 &p2_cu = positions_cu[points[segment_i] + 1]; + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + float3 &p2_cu = positions_cu[segment_i + 1]; const float3 direction = math::normalize(p2_cu - p1_cu); - const float expected_length_cu = expected_lengths_cu[points[segment_i]]; + const float expected_length_cu = expected_lengths_cu[segment_i]; p2_cu = p1_cu + direction * expected_length_cu; } } @@ -366,7 +405,7 @@ struct CombOperationExecutor { } }; -void CombOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void CombOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { CombOperationExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 2841a19d677..5a192346f39 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -35,6 +35,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -48,62 +50,62 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: - float2 brush_pos_prev_re_; - CurvesBrush3D brush_3d_; friend struct DeleteOperationExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; struct DeleteOperationExecutor { DeleteOperation *self_ = nullptr; - bContext *C_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float2 brush_pos_re_; - float2 brush_pos_prev_re_; float4x4 curves_to_world_mat_; float4x4 world_to_curves_mat_; - void execute(DeleteOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(DeleteOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { - BLI_SCOPED_DEFER([&]() { self.brush_pos_prev_re_ = stroke_extension.mouse_position; }); self_ = &self; - C_ = C; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + selected_curve_indices_.clear(); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); brush_pos_re_ = stroke_extension.mouse_position; - brush_pos_prev_re_ = stroke_extension.is_first ? stroke_extension.mouse_position : - self.brush_pos_prev_re_; curves_to_world_mat_ = object_->obmat; world_to_curves_mat_ = curves_to_world_mat_.inverted(); @@ -117,115 +119,152 @@ struct DeleteOperationExecutor { } } + Array<bool> curves_to_delete(curves_->curves_num(), false); if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->delete_projected(); + this->delete_projected_with_symmetry(curves_to_delete); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->delete_spherical(); + this->delete_spherical_with_symmetry(curves_to_delete); } else { BLI_assert_unreachable(); } + Vector<int64_t> indices; + const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + curves_->curves_range(), 4096, indices, [&](const int curve_i) { + return curves_to_delete[curve_i]; + }); + + curves_->remove_curves(mask); + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } - void delete_projected() + void delete_projected_with_symmetry(MutableSpan<bool> curves_to_delete) { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_projected(brush_transform, curves_to_delete); + } + } + + void delete_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_delete) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); Span<float3> positions_cu = curves_->positions(); - /* 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_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(point_range.size() - 1)) { - const float3 pos1_cu = positions_cu[point_range[segment_i]]; - const float3 pos2_cu = positions_cu[point_range[segment_i + 1]]; - - float2 pos1_re, pos2_re; - ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); - - const float dist = dist_seg_seg_v2( - pos1_re, pos2_re, brush_pos_prev_re_, brush_pos_re_); - if (dist <= brush_radius_re_) { - return true; - } - } - return false; - }); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); - curves_->remove_curves(curves_to_remove); + threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + if (points.size() == 1) { + const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + float2 pos_re; + ED_view3d_project_float_v2_m4(region_, pos_cu, pos_re, projection.values); + + if (math::distance_squared(brush_pos_re_, pos_re) <= brush_radius_sq_re) { + curves_to_delete[curve_i] = true; + } + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + + float2 pos1_re, pos2_re; + ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); + + const float dist_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, pos1_re, pos2_re); + if (dist_sq_re <= brush_radius_sq_re) { + curves_to_delete[curve_i] = true; + break; + } + } + } + }); } - void delete_spherical() + void delete_spherical_with_symmetry(MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); - float3 brush_start_wo, brush_end_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * self_->brush_3d_.position_cu, - brush_pos_prev_re_, - brush_start_wo); + float3 brush_wo; ED_view3d_win_to_3d(v3d_, region_, curves_to_world_mat_ * self_->brush_3d_.position_cu, brush_pos_re_, - brush_end_wo); - const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; - const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; + brush_wo); + const float3 brush_cu = world_to_curves_mat_ * brush_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->delete_spherical(brush_transform * brush_cu, curves_to_delete); + } + } - const float brush_radius_cu = self_->brush_3d_.radius_cu; + void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) + { + Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); - 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 points = curves_->points_for_curve(curve_i); - for (const int segment_i : IndexRange(points.size() - 1)) { - const float3 pos1_cu = positions_cu[points[segment_i]]; - const float3 pos2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_segment_cu, closest_brush_cu; - isect_seg_seg_v3(pos1_cu, - pos2_cu, - brush_start_cu, - brush_end_cu, - closest_segment_cu, - closest_brush_cu); - const float distance_to_brush_sq_cu = math::distance_squared(closest_segment_cu, - closest_brush_cu); - if (distance_to_brush_sq_cu > brush_radius_sq_cu) { - continue; - } - return true; + threading::parallel_for(curve_selection_.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + + if (points.size() == 1) { + const float3 &pos_cu = positions_cu[points.first()]; + const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); + if (distance_sq_cu < brush_radius_sq_cu) { + curves_to_delete[curve_i] = true; } - return false; - }); + continue; + } + + for (const int segment_i : points.drop_back(1)) { + const float3 &pos1_cu = positions_cu[segment_i]; + const float3 &pos2_cu = positions_cu[segment_i + 1]; - curves_->remove_curves(curves_to_remove); + const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); + if (distance_sq_cu > brush_radius_sq_cu) { + continue; + } + curves_to_delete[curve_i] = true; + break; + } + } + }); } void initialize_spherical_brush_reference_point() { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } } }; -void DeleteOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void DeleteOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { DeleteOperationExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 6228a643a76..d87828e7437 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -2,8 +2,6 @@ #include <algorithm> -#include "curves_sculpt_intern.hh" - #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" #include "BLI_kdtree.h" @@ -36,6 +34,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + #include "curves_sculpt_intern.hh" /** @@ -68,10 +68,10 @@ class CurvesEffect { */ class ShrinkCurvesEffect : public CurvesEffect { private: - Brush &brush_; + const Brush &brush_; public: - ShrinkCurvesEffect(Brush &brush) : brush_(brush) + ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { } @@ -203,10 +203,10 @@ class ExtrapolateCurvesEffect : public CurvesEffect { class ScaleCurvesEffect : public CurvesEffect { private: bool scale_up_; - Brush &brush_; + const Brush &brush_; public: - ScaleCurvesEffect(bool scale_up, Brush &brush) : scale_up_(scale_up), brush_(brush) + ScaleCurvesEffect(bool scale_up, const Brush &brush) : scale_up_(scale_up), brush_(brush) { } @@ -261,7 +261,7 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { { } - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -270,20 +270,25 @@ class CurvesEffectOperation : public CurvesSculptStrokeOperation { */ struct CurvesEffectOperationExecutor { CurvesEffectOperation *self_ = nullptr; - Depsgraph *depsgraph_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; - float brush_radius_sq_re_; + VArray<float> curve_selection_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; float4x4 curves_to_world_mat_; @@ -297,17 +302,19 @@ struct CurvesEffectOperationExecutor { Vector<float> move_distances_cu; }; - void execute(CurvesEffectOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(CurvesEffectOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_ = stroke_extension.mouse_position; }); self_ = &self; - depsgraph_ = CTX_data_depsgraph_pointer(C); - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); @@ -315,11 +322,17 @@ struct CurvesEffectOperationExecutor { return; } - CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt.paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); - brush_radius_sq_re_ = pow2f(brush_radius_re_); + curve_selection_factors_ = get_curves_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + + const CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; + brush_ = BKE_paint_brush_for_read(&curves_sculpt.paint); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + falloff_shape_ = eBrushFalloffShape(brush_->falloff_shape); curves_to_world_mat_ = object_->obmat; @@ -331,7 +344,13 @@ struct CurvesEffectOperationExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { if (std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C, *object_, stroke_extension.mouse_position, brush_radius_re_)) { + *depsgraph_, + *region_, + *v3d_, + *rv3d_, + *object_, + stroke_extension.mouse_position, + brush_radius_base_re_)) { self.brush_3d_ = *brush_3d; } } @@ -356,6 +375,7 @@ struct CurvesEffectOperationExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } @@ -367,62 +387,77 @@ struct CurvesEffectOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + Vector<float4x4> symmetry_brush_transforms_inv; + for (const float4x4 brush_transform : symmetry_brush_transforms) { + symmetry_brush_transforms_inv.append(brush_transform.inverted()); + } + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; + + const float curve_selection_factor = curve_selection_factors_[curve_i]; + float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float2 p1_re, p2_re; - ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); - ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); - - float2 closest_on_brush_re; - float2 closest_on_segment_re; - float lambda_on_brush; - float lambda_on_segment; - const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, - closest_on_segment_re, - &lambda_on_brush, - &lambda_on_segment, - brush_pos_start_re_, - brush_pos_end_re_, - p1_re, - p2_re); - - if (dist_to_brush_sq_re > brush_radius_sq_re_) { - continue; + for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { + for (const int segment_i : points.drop_back(1)) { + const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + + float2 p1_re, p2_re; + ED_view3d_project_float_v2_m4(region_, p1_cu, p1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, p2_cu, p2_re, projection.values); + + float2 closest_on_brush_re; + float2 closest_on_segment_re; + float lambda_on_brush; + float lambda_on_segment; + const float dist_to_brush_sq_re = closest_seg_seg_v2(closest_on_brush_re, + closest_on_segment_re, + &lambda_on_brush, + &lambda_on_segment, + brush_pos_start_re_, + brush_pos_end_re_, + p1_re, + p2_re); + + if (dist_to_brush_sq_re > brush_radius_sq_re) { + continue; + } + + const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_re, brush_radius_re); + const float weight = brush_strength_ * radius_falloff * curve_selection_factor; + + const float3 closest_on_segment_cu = math::interpolate( + p1_cu, p2_cu, lambda_on_segment); + + float3 brush_start_pos_wo, brush_end_pos_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_start_re_, + brush_start_pos_wo); + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * closest_on_segment_cu, + brush_pos_end_re_, + brush_end_pos_wo); + const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; + const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; + + const float move_distance_cu = weight * + math::distance(brush_start_pos_cu, brush_end_pos_cu); + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } - - const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; - - const float3 closest_on_segment_cu = math::interpolate(p1_cu, p2_cu, lambda_on_segment); - - float3 brush_start_pos_wo, brush_end_pos_wo; - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_start_re_, - brush_start_pos_wo); - ED_view3d_win_to_3d(v3d_, - region_, - curves_to_world_mat_ * closest_on_segment_cu, - brush_pos_end_re_, - brush_end_pos_wo); - const float3 brush_start_pos_cu = world_to_curves_mat_ * brush_start_pos_wo; - const float3 brush_end_pos_cu = world_to_curves_mat_ * brush_end_pos_wo; - - const float move_distance_cu = weight * - math::distance(brush_start_pos_cu, brush_end_pos_cu); - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); @@ -452,41 +487,53 @@ struct CurvesEffectOperationExecutor { const float3 brush_pos_end_cu = world_to_curves_mat_ * brush_pos_end_wo; const float3 brush_pos_diff_cu = brush_pos_end_cu - brush_pos_start_cu; const float brush_pos_diff_length_cu = math::length(brush_pos_diff_cu); - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { Influences &local_influences = influences_for_thread.local(); for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); - const int tot_segments = points.size() - 1; - float max_move_distance_cu = 0.0f; - for (const int segment_i : IndexRange(tot_segments)) { - const float3 &p1_cu = positions_cu[points[segment_i]]; - const float3 &p2_cu = positions_cu[points[segment_i] + 1]; - - float3 closest_on_segment_cu; - float3 closest_on_brush_cu; - isect_seg_seg_v3(p1_cu, - p2_cu, - brush_pos_start_cu, - brush_pos_end_cu, - closest_on_segment_cu, - closest_on_brush_cu); - - const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, - closest_on_brush_cu); - if (dist_to_brush_sq_cu > brush_radius_sq_cu) { - continue; - } - const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); - const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; + float max_move_distance_cu = 0.0f; - const float move_distance_cu = weight * brush_pos_diff_length_cu; - max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + const float curve_selection_factor = curve_selection_factors_[curve_i]; + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + const float3 brush_pos_start_transformed_cu = brush_transform * brush_pos_start_cu; + const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; + + for (const int segment_i : points.drop_back(1)) { + const float3 &p1_cu = positions_cu[segment_i]; + const float3 &p2_cu = positions_cu[segment_i + 1]; + + float3 closest_on_segment_cu; + float3 closest_on_brush_cu; + isect_seg_seg_v3(p1_cu, + p2_cu, + brush_pos_start_transformed_cu, + brush_pos_end_transformed_cu, + closest_on_segment_cu, + closest_on_brush_cu); + + const float dist_to_brush_sq_cu = math::distance_squared(closest_on_segment_cu, + closest_on_brush_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + continue; + } + + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); + const float radius_falloff = BKE_brush_curve_strength( + brush_, dist_to_brush_cu, brush_radius_cu); + const float weight = brush_strength_ * radius_falloff * curve_selection_factor; + + const float move_distance_cu = weight * brush_pos_diff_length_cu; + max_move_distance_cu = std::max(max_move_distance_cu, move_distance_cu); + } } if (max_move_distance_cu > 0.0f) { local_influences.curve_indices.append(curve_i); @@ -497,7 +544,7 @@ struct CurvesEffectOperationExecutor { } }; -void CurvesEffectOperation::on_stroke_extended(bContext *C, +void CurvesEffectOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) { CurvesEffectOperationExecutor executor; @@ -505,10 +552,10 @@ void CurvesEffectOperation::on_stroke_extended(bContext *C, } std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C) + const BrushStrokeMode brush_mode, const bContext &C) { - Scene &scene = *CTX_data_scene(C); - Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const Scene &scene = *CTX_data_scene(&C); + const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); const bool use_scale_uniform = brush.curves_sculpt_settings->flag & BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM; const bool use_grow = (brush_mode == BRUSH_STROKE_INVERT) == ((brush.flag & BRUSH_DIR_IN) != 0); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 03413221907..3635ad84c36 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -8,12 +8,19 @@ #include "paint_intern.h" #include "BLI_math_vector.hh" +#include "BLI_vector.hh" +#include "BLI_virtual_array.hh" +#include "BKE_attribute.h" #include "BKE_curves.hh" struct ARegion; struct RegionView3D; +struct Depsgraph; +struct View3D; struct Object; +struct Brush; +struct Scene; namespace blender::ed::sculpt_paint { @@ -22,23 +29,37 @@ using bke::CurvesGeometry; struct StrokeExtension { bool is_first; float2 mouse_position; + float pressure; }; +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension); +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension); + /** * 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; + virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, + ReportList *reports); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_grow_shrink_operation( - const BrushStrokeMode brush_mode, bContext *C); + const BrushStrokeMode brush_mode, const bContext &C); +std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation( + const BrushStrokeMode brush_mode, const bContext &C); struct CurvesBrush3D { float3 position_cu; @@ -48,9 +69,30 @@ struct CurvesBrush3D { /** * Find 3d brush position based on cursor position for curves sculpting. */ -std::optional<CurvesBrush3D> sample_curves_3d_brush(bContext &C, - Object &curves_object, +std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, + const ARegion ®ion, + const View3D &v3d, + const RegionView3D &rv3d, + const Object &curves_object, const float2 &brush_pos_re, - float brush_radius_re); + const float brush_radius_re); + +Vector<float4x4> get_symmetry_brush_transforms(eCurvesSymmetryType symmetry); + +/** + * Get the floating point selection on the curve domain, averaged from points if necessary. + */ +VArray<float> get_curves_selection(const Curves &curves_id); + +/** + * Get the floating point selection on the curve domain, copied from curves if necessary. + */ +VArray<float> get_point_selection(const Curves &curves_id); + +/** + * Find curves that have any point selected (a selection factor greater than zero), + * or curves that have their own selection factor greater than zero. + */ +IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices); } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 992ac77803a..aa2ec54c73f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -74,12 +74,42 @@ using blender::bke::CurvesGeometry; /** \name * SCULPT_CURVES_OT_brush_stroke * \{ */ -static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, - wmOperator *op) +float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension) { - const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op->ptr, "mode")); + if (BKE_brush_use_size_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_radius_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_size_get(&scene, &brush) * brush_radius_factor(brush, stroke_extension); +} + +float brush_strength_factor(const Brush &brush, const StrokeExtension &stroke_extension) +{ + if (BKE_brush_use_alpha_pressure(&brush)) { + return stroke_extension.pressure; + } + return 1.0f; +} + +float brush_strength_get(const Scene &scene, + const Brush &brush, + const StrokeExtension &stroke_extension) +{ + return BKE_brush_alpha_get(&scene, &brush) * brush_strength_factor(brush, stroke_extension); +} - Scene &scene = *CTX_data_scene(C); +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext &C, + wmOperator &op) +{ + const BrushStrokeMode mode = static_cast<BrushStrokeMode>(RNA_enum_get(op.ptr, "mode")); + + 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) { @@ -90,9 +120,11 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(); + return new_add_operation(C, op.reports); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); + case CURVES_SCULPT_TOOL_SELECTION_PAINT: + return new_selection_paint_operation(mode, C); } BLI_assert_unreachable(); return {}; @@ -128,17 +160,18 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); + stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); if (!op_data->operation) { stroke_extension.is_first = true; - op_data->operation = start_brush_operation(C, op); + op_data->operation = start_brush_operation(*C, *op); } else { stroke_extension.is_first = false; } if (op_data->operation) { - op_data->operation->on_stroke_extended(C, stroke_extension); + op_data->operation->on_stroke_extended(*C, stroke_extension); } } @@ -149,6 +182,12 @@ static void stroke_done(const bContext *C, PaintStroke *stroke) static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + if (brush == nullptr) { + return OPERATOR_CANCELLED; + } + SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__); op_data->stroke = paint_stroke_new(C, op, diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc new file mode 100644 index 00000000000..9a14f0cfef0 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_mask_ops.hh" + +#include "BKE_curves.hh" + +#include "curves_sculpt_intern.hh" + +namespace blender::ed::sculpt_paint { + +static VArray<float> get_curves_selection(const CurvesGeometry &curves, + const AttributeDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_CURVE: + return curves.selection_curve_float(); + case ATTR_DOMAIN_POINT: + return curves.adapt_domain( + curves.selection_point_float(), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + default: + BLI_assert_unreachable(); + return {}; + } +} + +VArray<float> get_curves_selection(const Curves &curves_id) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).curves_num()); + } + return get_curves_selection(CurvesGeometry::wrap(curves_id.geometry), + AttributeDomain(curves_id.selection_domain)); +} + +static VArray<float> get_point_selection(const CurvesGeometry &curves, + const AttributeDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_CURVE: + return curves.adapt_domain( + curves.selection_curve_float(), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + case ATTR_DOMAIN_POINT: + return curves.selection_point_float(); + default: + BLI_assert_unreachable(); + return {}; + } +} + +VArray<float> get_point_selection(const Curves &curves_id) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return VArray<float>::ForSingle(1.0f, CurvesGeometry::wrap(curves_id.geometry).points_num()); + } + return get_point_selection(CurvesGeometry::wrap(curves_id.geometry), + AttributeDomain(curves_id.selection_domain)); +} + +static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, + const AttributeDomain domain, + Vector<int64_t> &r_indices) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: { + const VArray<float> selection = curves.selection_point_float(); + if (selection.is_single()) { + return selection.get_internal_single() == 0.0f ? IndexMask(0) : + IndexMask(curves.curves_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 512, r_indices, [&](const int curve_i) { + for (const int i : curves.points_for_curve(curve_i)) { + if (selection[i] > 0.0f) { + return true; + } + } + return false; + }); + } + case ATTR_DOMAIN_CURVE: { + const VArray<float> selection = curves.selection_curve_float(); + if (selection.is_single()) { + return selection.get_internal_single() == 0.0f ? IndexMask(0) : + IndexMask(curves.curves_num()); + } + return index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 2048, r_indices, [&](const int i) { + return selection[i] > 0.0f; + }); + } + default: + BLI_assert_unreachable(); + return {}; + } +} + +IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices) +{ + if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) { + return CurvesGeometry::wrap(curves_id.geometry).curves_range(); + } + return retrieve_selected_curves(CurvesGeometry::wrap(curves_id.geometry), + AttributeDomain(curves_id.selection_domain), + r_indices); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc new file mode 100644 index 00000000000..250987466d5 --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <algorithm> +#include <numeric> + +#include "BLI_memory_utils.hh" +#include "BLI_task.hh" + +#include "DNA_brush_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "WM_api.h" + +#include "curves_sculpt_intern.hh" + +/** + * The code below uses a suffix naming convention to indicate the coordinate space: + * cu: Local space of the curves object that is being edited. + * wo: World space. + * re: 2D coordinates within the region. + */ + +namespace blender::ed::sculpt_paint { + +using bke::CurvesGeometry; + +class SelectionPaintOperation : public CurvesSculptStrokeOperation { + private: + bool use_select_; + bool clear_selection_; + + CurvesBrush3D brush_3d_; + + friend struct SelectionPaintOperationExecutor; + + public: + SelectionPaintOperation(const bool use_select, const bool clear_selection) + : use_select_(use_select), clear_selection_(clear_selection) + { + } + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; +}; + +struct SelectionPaintOperationExecutor { + SelectionPaintOperation *self_ = nullptr; + const bContext *C_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; + ARegion *region_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; + + Object *object_ = nullptr; + Curves *curves_id_ = nullptr; + CurvesGeometry *curves_ = nullptr; + + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; + float brush_strength_; + + float selection_goal_; + + float2 brush_pos_re_; + + float4x4 curves_to_world_mat_; + float4x4 world_to_curves_mat_; + + void execute(SelectionPaintOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) + { + self_ = &self; + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); + + curves_id_ = static_cast<Curves *>(object_->data); + curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_id_->flag |= CV_SCULPT_SELECTION_ENABLED; + + brush_ = BKE_paint_brush_for_read(&scene_->toolsettings->curves_sculpt->paint); + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + + brush_pos_re_ = stroke_extension.mouse_position; + + if (self.clear_selection_) { + if (stroke_extension.is_first) { + if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) { + curves_->selection_point_float_for_write().fill(0.0f); + } + else if (curves_id_->selection_domain == ATTR_DOMAIN_CURVE) { + curves_->selection_curve_float_for_write().fill(0.0f); + } + } + } + + curves_to_world_mat_ = object_->obmat; + world_to_curves_mat_ = curves_to_world_mat_.inverted(); + + const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( + brush_->falloff_shape); + + selection_goal_ = self_->use_select_ ? 1.0f : 0.0f; + + if (stroke_extension.is_first) { + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->initialize_spherical_brush_reference_point(); + } + } + + if (curves_id_->selection_domain == ATTR_DOMAIN_POINT) { + MutableSpan<float> selection = curves_->selection_point_float_for_write(); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->paint_point_selection_projected_with_symmetry(selection); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->paint_point_selection_spherical_with_symmetry(selection); + } + } + else { + MutableSpan<float> selection = curves_->selection_curve_float_for_write(); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + this->paint_curve_selection_projected_with_symmetry(selection); + } + else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + this->paint_curve_selection_spherical_with_symmetry(selection); + } + } + + /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because + * selection is handled as a generic attribute for now. */ + DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + ED_region_tag_redraw(region_); + } + + void paint_point_selection_projected_with_symmetry(MutableSpan<float> selection) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_point_selection_projected(brush_transform, selection); + } + } + + void paint_point_selection_projected(const float4x4 &brush_transform, + MutableSpan<float> selection) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { + for (const int point_i : point_range) { + const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + + /* Find the position of the point in screen space. */ + float2 pos_re; + ED_view3d_project_float_v2_m4(region_, pos_cu, pos_re, projection.values); + + const float distance_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_re = std::sqrt(distance_to_brush_sq_re); + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_re, brush_radius_re); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + selection[point_i] = math::interpolate(selection[point_i], selection_goal_, weight); + } + }); + } + + void paint_point_selection_spherical_with_symmetry(MutableSpan<float> selection) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + float3 brush_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_wo); + const float3 brush_cu = world_to_curves_mat_ * brush_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_point_selection_spherical(selection, brush_transform * brush_cu); + } + } + + void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) + { + Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { + for (const int i : point_range) { + const float3 pos_old_cu = positions_cu[i]; + + /* Compute distance to the brush. */ + const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu); + if (distance_to_brush_sq_cu > brush_radius_sq_cu) { + /* Ignore the point because it's too far away. */ + continue; + } + + const float distance_to_brush_cu = std::sqrt(distance_to_brush_sq_cu); + + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = BKE_brush_curve_strength( + brush_, distance_to_brush_cu, brush_radius_cu); + /* Combine the falloff and brush strength. */ + const float weight = brush_strength_ * radius_falloff; + + selection[i] = math::interpolate(selection[i], selection_goal_, weight); + } + }); + } + + void paint_curve_selection_projected_with_symmetry(MutableSpan<float> selection) + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_curve_selection_projected(brush_transform, selection); + } + } + + void paint_curve_selection_projected(const float4x4 &brush_transform, + MutableSpan<float> selection) + { + const Span<float3> positions_cu = curves_->positions(); + const float4x4 brush_transform_inv = brush_transform.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + + threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const float max_weight = threading::parallel_reduce( + curves_->points_for_curve(curve_i).drop_back(1), + 1024, + 0.0f, + [&](const IndexRange segment_range, const float init) { + float max_weight = init; + for (const int segment_i : segment_range) { + const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; + const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + + float2 pos1_re; + float2 pos2_re; + ED_view3d_project_float_v2_m4(region_, pos1_cu, pos1_re, projection.values); + ED_view3d_project_float_v2_m4(region_, pos2_cu, pos2_re, projection.values); + + const float distance_sq_re = dist_squared_to_line_segment_v2( + brush_pos_re_, pos1_re, pos2_re); + if (distance_sq_re > brush_radius_sq_re) { + continue; + } + const float radius_falloff = BKE_brush_curve_strength( + brush_, std::sqrt(distance_sq_re), brush_radius_re); + const float weight = brush_strength_ * radius_falloff; + max_weight = std::max(max_weight, weight); + } + return max_weight; + }, + [](float a, float b) { return std::max(a, b); }); + selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight); + } + }); + } + + void paint_curve_selection_spherical_with_symmetry(MutableSpan<float> selection) + { + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + + float3 brush_wo; + ED_view3d_win_to_3d(v3d_, + region_, + curves_to_world_mat_ * self_->brush_3d_.position_cu, + brush_pos_re_, + brush_wo); + const float3 brush_cu = world_to_curves_mat_ * brush_wo; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->paint_curve_selection_spherical(selection, brush_transform * brush_cu); + } + } + + void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) + { + const Span<float3> positions_cu = curves_->positions(); + + const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_sq_cu = pow2f(brush_radius_cu); + + threading::parallel_for(curves_->curves_range(), 1024, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const float max_weight = threading::parallel_reduce( + curves_->points_for_curve(curve_i).drop_back(1), + 1024, + 0.0f, + [&](const IndexRange segment_range, const float init) { + float max_weight = init; + for (const int segment_i : segment_range) { + const float3 &pos1_cu = positions_cu[segment_i]; + const float3 &pos2_cu = positions_cu[segment_i + 1]; + + const float distance_sq_cu = dist_squared_to_line_segment_v3( + brush_cu, pos1_cu, pos2_cu); + if (distance_sq_cu > brush_radius_sq_cu) { + continue; + } + const float radius_falloff = BKE_brush_curve_strength( + brush_, std::sqrt(distance_sq_cu), brush_radius_cu); + const float weight = brush_strength_ * radius_falloff; + max_weight = std::max(max_weight, weight); + } + return max_weight; + }, + [](float a, float b) { return std::max(a, b); }); + selection[curve_i] = math::interpolate(selection[curve_i], selection_goal_, max_weight); + } + }); + } + + void initialize_spherical_brush_reference_point() + { + std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); + if (brush_3d.has_value()) { + self_->brush_3d_ = *brush_3d; + } + } +}; + +void SelectionPaintOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) +{ + SelectionPaintOperationExecutor executor; + executor.execute(*this, C, stroke_extension); +} + +std::unique_ptr<CurvesSculptStrokeOperation> new_selection_paint_operation( + const BrushStrokeMode brush_mode, const bContext &C) +{ + Scene &scene = *CTX_data_scene(&C); + Brush &brush = *BKE_paint_brush(&scene.toolsettings->curves_sculpt->paint); + const bool use_select = ELEM(brush_mode, BRUSH_STROKE_INVERT) == + ((brush.flag & BRUSH_DIR_IN) != 0); + const bool clear_selection = use_select && brush_mode != BRUSH_STROKE_SMOOTH; + + return std::make_unique<SelectionPaintOperation>(use_select, clear_selection); +} + +} // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index 6d930d35f04..f834d1e350f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -37,6 +37,8 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "WM_api.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * - `cu`: Local space of the curves object that is being edited. @@ -61,7 +63,7 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { friend struct SnakeHookOperatorExecutor; public: - void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override; + void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; /** @@ -70,22 +72,28 @@ class SnakeHookOperation : public CurvesSculptStrokeOperation { */ struct SnakeHookOperatorExecutor { SnakeHookOperation *self_ = nullptr; - bContext *C_ = nullptr; - Scene *scene_ = nullptr; - Object *object_ = nullptr; + const Depsgraph *depsgraph_ = nullptr; + const Scene *scene_ = nullptr; ARegion *region_ = nullptr; - View3D *v3d_ = nullptr; - RegionView3D *rv3d_ = nullptr; + const View3D *v3d_ = nullptr; + const RegionView3D *rv3d_ = nullptr; - CurvesSculpt *curves_sculpt_ = nullptr; - Brush *brush_ = nullptr; - float brush_radius_re_; + const CurvesSculpt *curves_sculpt_ = nullptr; + const Brush *brush_ = nullptr; + float brush_radius_base_re_; + float brush_radius_factor_; float brush_strength_; + eBrushFalloffShape falloff_shape_; + Object *object_ = nullptr; Curves *curves_id_ = nullptr; CurvesGeometry *curves_ = nullptr; + VArray<float> curve_factors_; + Vector<int64_t> selected_curve_indices_; + IndexMask curve_selection_; + float4x4 curves_to_world_mat_; float4x4 world_to_curves_mat_; @@ -93,22 +101,28 @@ struct SnakeHookOperatorExecutor { float2 brush_pos_re_; float2 brush_pos_diff_re_; - void execute(SnakeHookOperation &self, bContext *C, const StrokeExtension &stroke_extension) + void execute(SnakeHookOperation &self, + const bContext &C, + const StrokeExtension &stroke_extension) { BLI_SCOPED_DEFER([&]() { self.last_mouse_position_re_ = stroke_extension.mouse_position; }); self_ = &self; - C_ = C; - scene_ = CTX_data_scene(C); - object_ = CTX_data_active_object(C); - region_ = CTX_wm_region(C); - v3d_ = CTX_wm_view3d(C); - rv3d_ = CTX_wm_region_view3d(C); + depsgraph_ = CTX_data_depsgraph_pointer(&C); + scene_ = CTX_data_scene(&C); + scene_ = CTX_data_scene(&C); + object_ = CTX_data_active_object(&C); + region_ = CTX_wm_region(&C); + v3d_ = CTX_wm_view3d(&C); + rv3d_ = CTX_wm_region_view3d(&C); curves_sculpt_ = scene_->toolsettings->curves_sculpt; - brush_ = BKE_paint_brush(&curves_sculpt_->paint); - brush_radius_re_ = BKE_brush_size_get(scene_, brush_); - brush_strength_ = BKE_brush_alpha_get(scene_, brush_); + brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); + + brush_radius_base_re_ = BKE_brush_size_get(scene_, brush_); + brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); + brush_strength_ = brush_strength_get(*scene_, *brush_, stroke_extension); + falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); curves_to_world_mat_ = object_->obmat; @@ -120,6 +134,9 @@ struct SnakeHookOperatorExecutor { return; } + curve_factors_ = get_curves_selection(*curves_id_); + curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + brush_pos_prev_re_ = self.last_mouse_position_re_; brush_pos_re_ = stroke_extension.mouse_position; brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; @@ -127,7 +144,7 @@ struct SnakeHookOperatorExecutor { if (stroke_extension.is_first) { if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { std::optional<CurvesBrush3D> brush_3d = sample_curves_3d_brush( - *C_, *object_, brush_pos_re_, brush_radius_re_); + *depsgraph_, *region_, *v3d_, *rv3d_, *object_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { self_->brush_3d_ = *brush_3d; } @@ -136,10 +153,10 @@ struct SnakeHookOperatorExecutor { } if (falloff_shape_ == PAINT_FALLOFF_SHAPE_SPHERE) { - this->spherical_snake_hook(); + this->spherical_snake_hook_with_symmetry(); } else if (falloff_shape_ == PAINT_FALLOFF_SHAPE_TUBE) { - this->projected_snake_hook(); + this->projected_snake_hook_with_symmetry(); } else { BLI_assert_unreachable(); @@ -147,49 +164,63 @@ struct SnakeHookOperatorExecutor { curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(region_); } - void projected_snake_hook() + void projected_snake_hook_with_symmetry() + { + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->projected_snake_hook(brush_transform); + } + } + + void projected_snake_hook(const float4x4 &brush_transform) { + const float4x4 brush_transform_inv = brush_transform.inverted(); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); + const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; + const float brush_radius_sq_re = pow2f(brush_radius_re); + threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; float2 old_pos_re; ED_view3d_project_float_v2_m4(region_, old_pos_cu, old_pos_re, projection.values); - const float distance_to_brush_re = math::distance(old_pos_re, brush_pos_prev_re_); - if (distance_to_brush_re > brush_radius_re_) { + const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + brush_pos_prev_re_); + if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; } const float radius_falloff = BKE_brush_curve_strength( - brush_, distance_to_brush_re, brush_radius_re_); - const float weight = brush_strength_ * radius_falloff; + brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); + const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; float3 new_position_wo; ED_view3d_win_to_3d( v3d_, region_, curves_to_world_mat_ * old_pos_cu, new_position_re, new_position_wo); - const float3 new_position_cu = world_to_curves_mat_ * new_position_wo; + const float3 new_position_cu = brush_transform * (world_to_curves_mat_ * new_position_wo); this->move_last_point_and_resample(positions_cu.slice(points), new_position_cu); } }); } - void spherical_snake_hook() + void spherical_snake_hook_with_symmetry() { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - float4x4 projection; ED_view3d_ob_project_mat_get(rv3d_, object_, projection.values); @@ -206,9 +237,23 @@ struct SnakeHookOperatorExecutor { brush_end_wo); const float3 brush_start_cu = world_to_curves_mat_ * brush_start_wo; const float3 brush_end_cu = world_to_curves_mat_ * brush_end_wo; - const float3 brush_diff_cu = brush_end_cu - brush_start_cu; - const float brush_radius_cu = self_->brush_3d_.radius_cu; + const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; + + const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_->symmetry)); + for (const float4x4 &brush_transform : symmetry_brush_transforms) { + this->spherical_snake_hook( + brush_transform * brush_start_cu, brush_transform * brush_end_cu, brush_radius_cu); + } + } + + void spherical_snake_hook(const float3 &brush_start_cu, + const float3 &brush_end_cu, + const float brush_radius_cu) + { + MutableSpan<float3> positions_cu = curves_->positions_for_write(); + const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) { @@ -227,7 +272,7 @@ struct SnakeHookOperatorExecutor { const float radius_falloff = BKE_brush_curve_strength( brush_, distance_to_brush_cu, brush_radius_cu); - const float weight = brush_strength_ * radius_falloff; + const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu; @@ -269,7 +314,8 @@ struct SnakeHookOperatorExecutor { } }; -void SnakeHookOperation::on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) +void SnakeHookOperation::on_stroke_extended(const bContext &C, + const StrokeExtension &stroke_extension) { SnakeHookOperatorExecutor executor; executor.execute(*this, C, stroke_extension); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index e442cd53639..b7ec427349f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -7,6 +7,7 @@ */ #include <float.h> +#include <limits.h> #include <math.h> #include <stdio.h> #include <string.h> @@ -35,12 +36,17 @@ #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_defs.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" +#include "DNA_object_enums.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_camera.h" #include "BKE_colorband.h" @@ -61,6 +67,8 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -76,14 +84,18 @@ #include "GPU_capabilities.h" #include "GPU_init_exit.h" +#include "NOD_shader.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" - -#include "NOD_shader.h" +#include "RNA_types.h" #include "IMB_colormanagement.h" @@ -4070,7 +4082,7 @@ typedef struct { static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone) { - MLoopUV *mloopuv_clone_base = NULL; + const MLoopUV *mloopuv_clone_base = NULL; /* use clone mtface? */ if (ps->do_layer_clone) { @@ -6472,6 +6484,38 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } +static CustomDataLayer *proj_paint_color_attribute_create(wmOperator *op, Object *ob) +{ + char name[MAX_NAME] = ""; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + AttributeDomain domain = ATTR_DOMAIN_POINT; + CustomDataType type = CD_PROP_COLOR; + + if (op) { + RNA_string_get(op->ptr, "name", name); + RNA_float_get_array(op->ptr, "color", color); + domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); + } + + ID *id = (ID *)ob->data; + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (!layer) { + return NULL; + } + + BKE_id_attributes_active_color_set(id, layer); + + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } + + BKE_object_attributes_active_color_fill(ob, color, false); + + return layer; +} + /** * Get a default color for the paint slot layer from a material's Principled BSDF. * @@ -6539,6 +6583,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Material *ma; Image *ima = NULL; + CustomDataLayer *layer = NULL; if (!ob) { return false; @@ -6551,7 +6596,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) int type = RNA_enum_get(op->ptr, "type"); bool is_data = (type > LAYER_BASE_COLOR); - bNode *imanode; + bNode *new_node; bNodeTree *ntree = ma->nodetree; if (!ntree) { @@ -6561,17 +6606,36 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - - ima = proj_paint_image_create(op, bmain, is_data); - imanode->id = &ima->id; - - nodeSetActive(ntree, imanode); + const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ? + (ePaintCanvasSource)RNA_enum_get(op->ptr, + "slot_type") : + PAINT_CANVAS_SOURCE_IMAGE; + + /* Create a new node. */ + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + ima = proj_paint_image_create(op, bmain, is_data); + new_node->id = &ima->id; + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: { + new_node = nodeAddStaticNode(C, ntree, SH_NODE_ATTRIBUTE); + if ((layer = proj_paint_color_attribute_create(op, ob))) { + BLI_strncpy_utf8( + ((NodeShaderAttribute *)new_node->storage)->name, layer->name, MAX_NAME); + } + break; + } + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + return false; + } + nodeSetActive(ntree, new_node); /* Connect to first available principled BSDF node. */ bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + bNode *out_node = new_node; if (in_node != NULL) { bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); @@ -6634,6 +6698,11 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); } + if (layer) { + BKE_texpaint_slot_refresh_cache(scene, ma, ob); + DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data); + } DEG_id_tag_update(&ntree->id, 0); DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); @@ -6695,6 +6764,44 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, return WM_operator_props_dialog_popup(C, op, 300); } +static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + Object *ob = ED_object_active_context(C); + ePaintCanvasSource slot_type = PAINT_CANVAS_SOURCE_IMAGE; + + if (ob->mode == OB_MODE_SCULPT) { + slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type"); + uiItemR(layout, op->ptr, "slot_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + } + + uiItemR(layout, op->ptr, "name", 0, NULL, ICON_NONE); + + switch (slot_type) { + case PAINT_CANVAS_SOURCE_IMAGE: { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE); + + uiItemR(layout, op->ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "generated_type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "float", 0, NULL, ICON_NONE); + break; + } + case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE: + uiItemR(layout, op->ptr, "domain", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "data_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + break; + case PAINT_CANVAS_SOURCE_MATERIAL: + BLI_assert_unreachable(); + break; + } + + uiItemR(layout, op->ptr, "color", 0, NULL, ICON_NONE); +} + #define IMA_DEF_NAME N_("Untitled") void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) @@ -6702,40 +6809,92 @@ void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + static const EnumPropertyItem slot_type_items[3] = { + {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""}, + {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem domain_items[3] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem attribute_type_items[3] = { + {CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; + ot->name = "Add Paint Slot"; + ot->description = "Add a paint slot"; ot->idname = "PAINT_OT_add_texture_paint_slot"; /* api callbacks */ ot->invoke = texture_paint_add_texture_paint_slot_invoke; ot->exec = texture_paint_add_texture_paint_slot_exec; ot->poll = ED_operator_object_active_editable_mesh; + ot->ui = texture_paint_add_texture_paint_slot_ui; /* flags */ ot->flag = OPTYPE_UNDO; - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + /* Shared Properties */ + prop = RNA_def_enum(ot->srna, + "type", + layer_type_items, + 0, + "Material Layer Type", + "Material layer type of new paint slot"); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_enum( + ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot"); + + prop = RNA_def_string( + ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_color( ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); RNA_def_property_float_array_default(prop, default_color); + + /* Image Properties */ + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean( ot->srna, "float", 0, "32-bit Float", "Create image with 32-bit floating-point bit depth"); + + /* Color Attribute Properties */ + RNA_def_enum(ot->srna, + "domain", + domain_items, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_enum(ot->srna, + "data_type", + attribute_type_items, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index f13c34e2030..fb1c8ceaa1a 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -4144,13 +4144,7 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_ } /** - * Fills the object's active color atribute layer with the fill color. - * - * \param[in] ob: The object. - * \param[in] fill_color: The fill color. - * \param[in] only_selected: Limit the fill to selected faces or vertices. - * - * \return #true if successful. + * See doc-string for #BKE_object_attributes_active_color_fill. */ static bool paint_object_attributes_active_color_fill_ex(Object *ob, ColorPaint4f fill_color, diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 34647cd9bf6..2c6c4c82676 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3261,8 +3261,8 @@ static void do_brush_action(Sculpt *sd, } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } - - if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { + const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob); + if (use_pixels) { sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); } @@ -3315,16 +3315,18 @@ static void do_brush_action(Sculpt *sd, } float location[3]; - SculptThreadedTaskData task_data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + if (!use_pixels) { + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + } if (sculpt_brush_needs_normal(ss, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); @@ -5289,6 +5291,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + ToolSettings *tool_settings = CTX_data_tool_settings(C); /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the * canvas it is painting on. (ref. use_sculpt_texture_paint). */ @@ -5306,7 +5309,15 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); - SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + /* Setup the correct undo system. Image painting and sculpting are mutual exclusive. + * Color attributes are part of the sculpting undo system. */ + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT); + } + else { + SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); + } return true; } @@ -5433,7 +5444,13 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - SCULPT_undo_push_end(ob); + if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT && + SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) { + ED_image_undo_push_end(); + } + else { + SCULPT_undo_push_end(ob); + } if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 7171c241534..d03c5ecab4d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -60,7 +60,7 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) { - int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); if (!face_sets) { return SCULPT_FACE_SET_NONE; } @@ -150,25 +150,22 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.index, - thread_id); - - if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); - } + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + thread_id); + + if (fade > 0.05f) { + SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index f9633c91087..5fa115ed629 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1652,11 +1652,11 @@ void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings, int totnode) ATTR_NONNULL(); /** - * @brief Get the image canvas for painting on the given object. + * \brief Get the image canvas for painting on the given object. * - * @return #true if an image is found. The #r_image and #r_image_user fields are filled with the + * \return #true if an image is found. The #r_image and #r_image_user fields are filled with the * image and image user. Returns false when the image isn't found. In the later case the r_image - * and r_image_user are set to nullptr/NULL. + * and r_image_user are set to NULL. */ bool SCULPT_paint_image_canvas_get(struct PaintModeSettings *paint_mode_settings, struct Object *ob, diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 84123ff3186..eef344c3d90 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -637,16 +637,16 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (MPropCol_layer_n == -1) { return OPERATOR_CANCELLED; } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); + const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; float srgb_color[4]; linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); loopcols[loop_index].r = (char)(srgb_color[0] * 255); @@ -711,7 +711,8 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) if (mloopcol_layer_n == -1) { return OPERATOR_CANCELLED; } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); + const MLoopCol *loopcols = CustomData_get_layer_n( + &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); if (MPropCol_layer_n == -1) { @@ -719,14 +720,14 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) } MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); + const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); + const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); for (int i = 0; i < mesh->totpoly; i++) { - MPoly *c_poly = &polys[i]; + const MPoly *c_poly = &polys[i]; for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; - MLoop *c_loop = &loops[c_poly->loopstart + j]; + const MLoop *c_loop = &loops[c_poly->loopstart + j]; vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index df1ccc0fbe9..975a8f21aaf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -360,6 +360,86 @@ static void do_paint_pixels(void *__restrict userdata, node_data.flags.dirty |= pixels_updated; } +static void undo_region_tiles( + ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) +{ + int srcx = 0, srcy = 0; + 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); + *tx = (x >> ED_IMAGE_UNDO_TILE_BITS); + *ty = (y >> ED_IMAGE_UNDO_TILE_BITS); +} + +static void push_undo(const NodeData &node_data, + Image &image, + ImageUser &image_user, + const image::ImageTileWrapper &image_tile, + ImBuf &image_buffer, + ImBuf **tmpibuf) +{ + for (const UDIMTileUndo &tile_undo : node_data.undo_regions) { + if (tile_undo.tile_number != image_tile.get_tile_number()) { + continue; + } + int tilex, tiley, tilew, tileh; + ListBase *undo_tiles = ED_image_paint_tile_list_get(); + undo_region_tiles(&image_buffer, + tile_undo.region.xmin, + tile_undo.region.ymin, + BLI_rcti_size_x(&tile_undo.region), + BLI_rcti_size_y(&tile_undo.region), + &tilex, + &tiley, + &tilew, + &tileh); + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + ED_image_paint_tile_push(undo_tiles, + &image, + &image_buffer, + tmpibuf, + &image_user, + tx, + ty, + nullptr, + nullptr, + true, + true); + } + } + } +} + +static void do_push_undo_tile(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata); + PBVHNode *node = data->nodes[n]; + + NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node); + Image *image = data->image_data.image; + ImageUser *image_user = data->image_data.image_user; + + ImBuf *tmpibuf = nullptr; + ImageUser local_image_user = *image_user; + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + image::ImageTileWrapper image_tile(tile); + local_image_user.tile = image_tile.get_tile_number(); + ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &local_image_user, nullptr); + if (image_buffer == nullptr) { + continue; + } + + push_undo(node_data, *image, *image_user, image_tile, *image_buffer, &tmpibuf); + BKE_image_release_ibuf(image, image_buffer, nullptr); + } + if (tmpibuf) { + IMB_freeImBuf(tmpibuf); + } +} + static void do_mark_dirty_regions(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -380,8 +460,9 @@ bool SCULPT_paint_image_canvas_get(PaintModeSettings *paint_mode_settings, Image **r_image, ImageUser **r_image_user) { - BLI_assert(r_image); - BLI_assert(r_image_user); + *r_image = nullptr; + *r_image_user = nullptr; + ImageData image_data; if (!ImageData::init_active_image(ob, &image_data, paint_mode_settings)) { return false; @@ -421,6 +502,7 @@ void SCULPT_do_paint_brush_image( TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings); BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings); TaskParallelSettings settings_flush; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 54678c21cb8..4588930bdee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1266,7 +1266,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); - int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); + const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { unode->face_sets[i] = face_sets[i]; } diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index e24d461019b..72df2b74b11 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -303,7 +303,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->pattern_corners[a][1] *= scale_y; } - BKE_tracking_marker_clamp(cb->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } @@ -319,7 +319,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) sub_v2_v2v2(cb->marker->search_min, delta, side); add_v2_v2v2(cb->marker->search_max, delta, side); - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(cb->marker); ok = true; } @@ -340,7 +340,7 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) cb->marker->search_max[0] += dim[0]; cb->marker->search_max[1] += dim[1]; - BKE_tracking_marker_clamp(cb->marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(cb->marker); ok = true; } diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index dd01c095479..7f9cf61b748 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -187,8 +187,10 @@ void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene); /* tracking_ops.c */ -struct MovieTrackingTrack *tracking_marker_check_slide( - struct bContext *C, const struct wmEvent *event, int *r_area, int *r_action, int *r_corner); +/* Find track which can be slid in a proximity of the given event. + * Uses the same distance tolerance rule as the "Slide Marker" operator. */ +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]); void CLIP_OT_add_marker(struct wmOperatorType *ot); void CLIP_OT_add_marker_at_click(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 91fef23019c..5f52e1a3071 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -619,6 +619,44 @@ static void clip_dropboxes(void) WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL); } +static bool clip_set_region_visible(const bContext *C, + ARegion *region, + const bool is_visible, + const short alignment, + const bool view_all_on_show) +{ + bool view_changed = false; + + if (is_visible) { + if (region && (region->flag & RGN_FLAG_HIDDEN)) { + region->flag &= ~RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + if (view_all_on_show) { + region->v2d.cur = region->v2d.tot; + } + view_changed = true; + } + if (region && region->alignment != alignment) { + region->alignment = alignment; + view_changed = true; + } + } + else { + if (region && !(region->flag & RGN_FLAG_HIDDEN)) { + region->flag |= RGN_FLAG_HIDDEN; + region->v2d.flag &= ~V2D_IS_INIT; + WM_event_remove_handlers((bContext *)C, ®ion->handlers); + view_changed = true; + } + if (region && region->alignment != RGN_ALIGN_NONE) { + region->alignment = RGN_ALIGN_NONE; + view_changed = true; + } + } + + return view_changed; +} + static void clip_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); @@ -662,127 +700,14 @@ static void clip_refresh(const bContext *C, ScrArea *area) break; } - if (main_visible) { - if (region_main && (region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag &= ~RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_main && !(region_main->flag & RGN_FLAG_HIDDEN)) { - region_main->flag |= RGN_FLAG_HIDDEN; - region_main->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_main->handlers); - view_changed = true; - } - if (region_main && region_main->alignment != RGN_ALIGN_NONE) { - region_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (properties_visible) { - if (region_properties && (region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag &= ~RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_RIGHT) { - region_properties->alignment = RGN_ALIGN_RIGHT; - view_changed = true; - } - } - else { - if (region_properties && !(region_properties->flag & RGN_FLAG_HIDDEN)) { - region_properties->flag |= RGN_FLAG_HIDDEN; - region_properties->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_properties->handlers); - view_changed = true; - } - if (region_properties && region_properties->alignment != RGN_ALIGN_NONE) { - region_properties->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (tools_visible) { - if (region_tools && (region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag &= ~RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_LEFT) { - region_tools->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_tools && !(region_tools->flag & RGN_FLAG_HIDDEN)) { - region_tools->flag |= RGN_FLAG_HIDDEN; - region_tools->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_tools->handlers); - view_changed = true; - } - if (region_tools && region_tools->alignment != RGN_ALIGN_NONE) { - region_tools->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (preview_visible) { - if (region_preview && (region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag &= ~RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - region_preview->v2d.cur = region_preview->v2d.tot; - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - else { - if (region_preview && !(region_preview->flag & RGN_FLAG_HIDDEN)) { - region_preview->flag |= RGN_FLAG_HIDDEN; - region_preview->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_preview->handlers); - view_changed = true; - } - if (region_preview && region_preview->alignment != RGN_ALIGN_NONE) { - region_preview->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } - - if (channels_visible) { - if (region_channels && (region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag &= ~RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_LEFT) { - region_channels->alignment = RGN_ALIGN_LEFT; - view_changed = true; - } - } - else { - if (region_channels && !(region_channels->flag & RGN_FLAG_HIDDEN)) { - region_channels->flag |= RGN_FLAG_HIDDEN; - region_channels->v2d.flag &= ~V2D_IS_INIT; - WM_event_remove_handlers((bContext *)C, ®ion_channels->handlers); - view_changed = true; - } - if (region_channels && region_channels->alignment != RGN_ALIGN_NONE) { - region_channels->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - } + view_changed |= clip_set_region_visible(C, region_main, main_visible, RGN_ALIGN_NONE, false); + view_changed |= clip_set_region_visible( + C, region_properties, properties_visible, RGN_ALIGN_RIGHT, false); + view_changed |= clip_set_region_visible(C, region_tools, tools_visible, RGN_ALIGN_LEFT, false); + view_changed |= clip_set_region_visible( + C, region_preview, preview_visible, RGN_ALIGN_NONE, true); + view_changed |= clip_set_region_visible( + C, region_channels, channels_visible, RGN_ALIGN_LEFT, false); if (view_changed) { ED_area_init(wm, window, area); diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index e7bdbfe7c68..ca224b04da5 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -336,35 +336,44 @@ void CLIP_OT_delete_marker(wmOperatorType *ot) /** \name Slide Marker Operator * \{ */ -enum { - SLIDE_ACTION_POS = 0, +typedef enum eSlideAction { + SLIDE_ACTION_NONE, + + SLIDE_ACTION_POS, SLIDE_ACTION_SIZE, SLIDE_ACTION_OFFSET, SLIDE_ACTION_TILT_SIZE, -}; +} eSlideAction; typedef struct { - short area, action; + short area; + eSlideAction action; MovieTrackingTrack *track; MovieTrackingMarker *marker; int mval[2]; int width, height; - float *min, *max, *pos, *offset, (*corners)[2]; - float spos[2]; + float *min, *max, *pos, (*corners)[2]; bool lock, accurate; /* Data to restore on cancel. */ - float old_search_min[2], old_search_max[2], old_pos[2], old_offset[2]; + float old_search_min[2], old_search_max[2], old_pos[2]; float old_corners[4][2]; float (*old_markers)[2]; } SlideMarkerData; -static void slide_marker_tilt_slider(const MovieTrackingMarker *marker, float r_slider[2]) +static void slide_marker_tilt_slider_relative(const float pattern_corners[4][2], float r_slider[2]) +{ + add_v2_v2v2(r_slider, pattern_corners[1], pattern_corners[2]); +} + +static void slide_marker_tilt_slider(const float marker_pos[2], + const float pattern_corners[4][2], + float r_slider[2]) { - add_v2_v2v2(r_slider, marker->pattern_corners[1], marker->pattern_corners[2]); - add_v2_v2(r_slider, marker->pos); + slide_marker_tilt_slider_relative(pattern_corners, r_slider); + add_v2_v2(r_slider, marker_pos); } static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, @@ -373,7 +382,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, const wmEvent *event, int area, int corner, - int action, + eSlideAction action, int width, int height) { @@ -389,29 +398,14 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, if (area == TRACK_AREA_POINT) { data->pos = marker->pos; - data->offset = track->offset; } else if (area == TRACK_AREA_PAT) { - if (action == SLIDE_ACTION_SIZE) { - data->corners = marker->pattern_corners; - } - else if (action == SLIDE_ACTION_OFFSET) { - data->pos = marker->pos; - data->offset = track->offset; - data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, - "slide markers"); - for (int a = 0; a < track->markersnr; a++) { - copy_v2_v2(data->old_markers[a], track->markers[a].pos); - } - } - else if (action == SLIDE_ACTION_POS) { + if (action == SLIDE_ACTION_POS) { data->corners = marker->pattern_corners; data->pos = marker->pattern_corners[corner]; - copy_v2_v2(data->spos, data->pos); } else if (action == SLIDE_ACTION_TILT_SIZE) { data->corners = marker->pattern_corners; - slide_marker_tilt_slider(marker, data->spos); } } else if (area == TRACK_AREA_SEARCH) { @@ -434,7 +428,6 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, copy_v2_v2(data->old_search_min, marker->search_min); copy_v2_v2(data->old_search_max, marker->search_max); copy_v2_v2(data->old_pos, marker->pos); - copy_v2_v2(data->old_offset, track->offset); return data; } @@ -497,7 +490,7 @@ static int mouse_to_tilt_distance_squared(const MovieTrackingMarker *marker, int height) { float slider[2]; - slide_marker_tilt_slider(marker, slider); + slide_marker_tilt_slider(marker->pos, marker->pattern_corners, slider); return mouse_to_slide_zone_distance_squared(co, slider, width, height); } @@ -534,117 +527,96 @@ static bool slide_check_corners(float (*corners)[2]) return true; } -MovieTrackingTrack *tracking_marker_check_slide( - bContext *C, const wmEvent *event, int *r_area, int *r_action, int *r_corner) +static MovieTrackingTrack *tracking_marker_check_slide( + bContext *C, const float co[2], int *r_area, eSlideAction *r_action, int *r_corner) { const float distance_clip_squared = 12.0f * 12.0f; SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTrackingTrack *track; - int width, height; - float co[2]; ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); float global_min_distance_squared = FLT_MAX; - /* Sliding zone designator which is the closest to the mouse - * across all the tracks. - */ - int min_action = -1, min_area = 0, min_corner = -1; + /* Sliding zone designator which is the closest to the mouse across all the tracks. */ + eSlideAction min_action; + int min_area = 0, min_corner = -1; MovieTrackingTrack *min_track = NULL; + int width, height; ED_space_clip_get_size(sc, &width, &height); - if (width == 0 || height == 0) { return NULL; } - ED_clip_mouse_pos(sc, region, event->mval, co); + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + if (!TRACK_VIEW_SELECTED(sc, track) || (track->flag & TRACK_LOCKED)) { + continue; + } - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - /* Sliding zone designator which is the closest to the mouse for - * the current tracks. - */ - float min_distance_squared = FLT_MAX; - int action = -1, area = 0, corner = -1; - - if ((marker->flag & MARKER_DISABLED) == 0) { - float distance_squared; - - /* We start checking with whether the mouse is close enough - * to the pattern offset area. - */ - distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); - area = TRACK_AREA_POINT; - action = SLIDE_ACTION_POS; + const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + if (marker->flag & MARKER_DISABLED) { + continue; + } - /* NOTE: All checks here are assuming there's no maximum distance - * limit, so checks are quite simple here. - * Actual distance clipping happens later once all the sliding - * zones are checked. - */ + /* We start checking with whether the mouse is close enough to the pattern offset area. */ + float distance_squared = mouse_to_offset_distance_squared(track, marker, co, width, height); + + /* Sliding zone designator which is the closest to the mouse for the current tracks. + * + * NOTE: All checks here are assuming there's no maximum distance limit, so checks are quite + * simple here. Actual distance clipping happens later once all the sliding zones are checked. + */ + float min_distance_squared = distance_squared; + int area = TRACK_AREA_POINT; + int action = SLIDE_ACTION_POS; + int corner = -1; + + /* If search area is visible, check how close to its sliding zones mouse is. */ + if (sc->flag & SC_SHOW_MARKER_SEARCH) { + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_OFFSET; min_distance_squared = distance_squared; + } - /* If search area is visible, check how close to its sliding - * zones mouse is. - */ - if (sc->flag & SC_SHOW_MARKER_SEARCH) { - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 1, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_OFFSET; - min_distance_squared = distance_squared; - } - - distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_SEARCH; - action = SLIDE_ACTION_SIZE; - min_distance_squared = distance_squared; - } - } - - /* If pattern area is visible, check which corner is closest to - * the mouse. - */ - if (sc->flag & SC_SHOW_MARKER_PATTERN) { - int current_corner = -1; - distance_squared = mouse_to_closest_pattern_corner_distance_squared( - marker, co, width, height, ¤t_corner); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_POS; - corner = current_corner; - min_distance_squared = distance_squared; - } + distance_squared = mouse_to_search_corner_distance_squared(marker, co, 0, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_SEARCH; + action = SLIDE_ACTION_SIZE; + min_distance_squared = distance_squared; + } + } - /* Here we also check whether the mouse is actually closer to - * the widget which controls scale and tilt. - */ - distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); - if (distance_squared < min_distance_squared) { - area = TRACK_AREA_PAT; - action = SLIDE_ACTION_TILT_SIZE; - min_distance_squared = distance_squared; - } - } + /* If pattern area is visible, check which corner is closest to the mouse. */ + if (sc->flag & SC_SHOW_MARKER_PATTERN) { + int current_corner = -1; + distance_squared = mouse_to_closest_pattern_corner_distance_squared( + marker, co, width, height, ¤t_corner); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_POS; + corner = current_corner; + min_distance_squared = distance_squared; + } - if (min_distance_squared < global_min_distance_squared) { - min_area = area; - min_action = action; - min_corner = corner; - min_track = track; - global_min_distance_squared = min_distance_squared; - } + /* Here we also check whether the mouse is actually closer to the widget which controls scale + * and tilt. */ + distance_squared = mouse_to_tilt_distance_squared(marker, co, width, height); + if (distance_squared < min_distance_squared) { + area = TRACK_AREA_PAT; + action = SLIDE_ACTION_TILT_SIZE; + min_distance_squared = distance_squared; } } - track = track->next; + if (min_distance_squared < global_min_distance_squared) { + min_area = area; + min_action = action; + min_corner = corner; + min_track = track; + global_min_distance_squared = min_distance_squared; + } } if (global_min_distance_squared < distance_clip_squared / sc->zoom) { @@ -659,9 +631,16 @@ MovieTrackingTrack *tracking_marker_check_slide( } return min_track; } + return NULL; } +struct MovieTrackingTrack *tracking_find_slidable_track_in_proximity(struct bContext *C, + const float co[2]) +{ + return tracking_marker_check_slide(C, co, NULL, NULL, NULL); +} + static void *slide_marker_customdata(bContext *C, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); @@ -672,7 +651,8 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) float co[2]; void *customdata = NULL; int framenr = ED_space_clip_get_clip_frame_number(sc); - int area, action, corner; + eSlideAction action; + int area, corner; ED_space_clip_get_size(sc, &width, &height); @@ -682,7 +662,7 @@ static void *slide_marker_customdata(bContext *C, const wmEvent *event) ED_clip_mouse_pos(sc, region, event->mval, co); - track = tracking_marker_check_slide(C, event, &area, &action, &corner); + track = tracking_marker_check_slide(C, co, &area, &action, &corner); if (track != NULL) { MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); customdata = create_slide_marker_data( @@ -718,14 +698,12 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, const wmEvent *event static void cancel_mouse_slide(SlideMarkerData *data) { - MovieTrackingTrack *track = data->track; MovieTrackingMarker *marker = data->marker; memcpy(marker->pattern_corners, data->old_corners, sizeof(marker->pattern_corners)); copy_v2_v2(marker->search_min, data->old_search_min); copy_v2_v2(marker->search_max, data->old_search_max); copy_v2_v2(marker->pos, data->old_pos); - copy_v2_v2(track->offset, data->old_offset); if (data->old_markers != NULL) { for (int a = 0; a < data->track->markersnr; a++) { @@ -765,7 +743,6 @@ static void free_slide_data(SlideMarkerData *data) static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); - ARegion *region = CTX_wm_region(C); SlideMarkerData *data = (SlideMarkerData *)op->customdata; float dx, dy, mdelta[2]; @@ -804,110 +781,66 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (data->area == TRACK_AREA_POINT) { - if (data->action == SLIDE_ACTION_OFFSET) { - data->offset[0] = data->old_offset[0] + dx; - data->offset[1] = data->old_offset[1] + dy; - } - else { - data->pos[0] = data->old_pos[0] + dx; - data->pos[1] = data->old_pos[1] + dy; - } + data->pos[0] += dx; + data->pos[1] += dy; WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); DEG_id_tag_update(&sc->clip->id, 0); } else if (data->area == TRACK_AREA_PAT) { - if (data->action == SLIDE_ACTION_SIZE) { - float start[2], end[2]; - float scale; - - ED_clip_point_stable_pos(sc, region, data->mval[0], data->mval[1], &start[0], &start[1]); - - sub_v2_v2(start, data->old_pos); - - if (len_squared_v2(start) != 0.0f) { - float mval[2]; - - if (data->accurate) { - mval[0] = data->mval[0] + (event->mval[0] - data->mval[0]) / 5.0f; - mval[1] = data->mval[1] + (event->mval[1] - data->mval[1]) / 5.0f; - } - else { - mval[0] = event->mval[0]; - mval[1] = event->mval[1]; - } + if (data->action == SLIDE_ACTION_POS) { + float prev_pos[2]; + copy_v2_v2(prev_pos, data->pos); - ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &end[0], &end[1]); - - sub_v2_v2(end, data->old_pos); - scale = len_v2(end) / len_v2(start); - - if (scale > 0.0f) { - for (int a = 0; a < 4; a++) { - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); - } - } - } - - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); - } - else if (data->action == SLIDE_ACTION_OFFSET) { - const float d[2] = {dx, dy}; - for (int a = 0; a < data->track->markersnr; a++) { - add_v2_v2v2(data->track->markers[a].pos, data->old_markers[a], d); - } - sub_v2_v2v2(data->offset, data->old_offset, d); - } - else if (data->action == SLIDE_ACTION_POS) { - float spos[2]; - - copy_v2_v2(spos, data->pos); - - data->pos[0] = data->spos[0] + dx; - data->pos[1] = data->spos[1] + dy; + data->pos[0] += dx; + data->pos[1] += dy; if (!slide_check_corners(data->corners)) { - copy_v2_v2(data->pos, spos); + copy_v2_v2(data->pos, prev_pos); } - /* Currently only patterns are allowed to have such - * combination of event and data. - */ - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + /* Allow pattern to be arbitrary size and resize search area if needed. */ + BKE_tracking_marker_clamp_search_size(data->marker); } else if (data->action == SLIDE_ACTION_TILT_SIZE) { - const float mouse_delta[2] = {dx, dy}; - - /* Vector which connects marker position with tilt/scale sliding area before sliding - * began. */ - float start[2]; - sub_v2_v2v2(start, data->spos, data->old_pos); - start[0] *= data->width; - start[1] *= data->height; - - /* Vector which connects marker position with tilt/scale sliding area with the sliding - * delta applied. */ - float end[2]; - add_v2_v2v2(end, data->spos, mouse_delta); - sub_v2_v2(end, data->old_pos); - end[0] *= data->width; - end[1] *= data->height; + const float delta[2] = {dx, dy}; + + /* Slider position relative to the marker position using current state of pattern + * corners. */ + float slider[2]; + slide_marker_tilt_slider_relative(data->corners, slider); + + /* Vector which connects marker position with the slider state at the current corners + * state. + * The coordinate is in the pixel space. */ + float start_px[2]; + copy_v2_v2(start_px, slider); + start_px[0] *= data->width; + start_px[1] *= data->height; + + /* Vector which connects marker position with the slider state with the new mouse delta + * taken into account. + * The coordinate is in the pixel space. */ + float end_px[2]; + add_v2_v2v2(end_px, slider, delta); + end_px[0] *= data->width; + end_px[1] *= data->height; float scale = 1.0f; - if (len_squared_v2(start) != 0.0f) { - scale = len_v2(end) / len_v2(start); + if (len_squared_v2(start_px) != 0.0f) { + scale = len_v2(end_px) / len_v2(start_px); if (scale < 0.0f) { scale = 0.0; } } - const float angle = -angle_signed_v2v2(start, end); + const float angle = -angle_signed_v2v2(start_px, end_px); for (int a = 0; a < 4; a++) { float vec[2]; - mul_v2_v2fl(data->corners[a], data->old_corners[a], scale); + mul_v2_fl(data->corners[a], scale); copy_v2_v2(vec, data->corners[a]); vec[0] *= data->width; @@ -917,30 +850,32 @@ static int slide_marker_modal(bContext *C, wmOperator *op, const wmEvent *event) data->corners[a][1] = (vec[1] * cosf(angle) + vec[0] * sinf(angle)) / data->height; } - BKE_tracking_marker_clamp(data->marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(data->marker); } } else if (data->area == TRACK_AREA_SEARCH) { if (data->action == SLIDE_ACTION_SIZE) { - data->min[0] = data->old_search_min[0] - dx; - data->max[0] = data->old_search_max[0] + dx; + data->min[0] -= dx; + data->min[1] += dy; - data->min[1] = data->old_search_min[1] + dy; - data->max[1] = data->old_search_max[1] - dy; + data->max[0] += dx; + data->max[1] -= dy; - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_DIM); - } - else if (data->area == TRACK_AREA_SEARCH) { - const float d[2] = {dx, dy}; - add_v2_v2v2(data->min, data->old_search_min, d); - add_v2_v2v2(data->max, data->old_search_max, d); + BKE_tracking_marker_clamp_search_size(data->marker); } + else if (data->action == SLIDE_ACTION_OFFSET) { + const float delta[2] = {dx, dy}; + add_v2_v2(data->min, delta); + add_v2_v2(data->max, delta); - BKE_tracking_marker_clamp(data->marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(data->marker); + } } data->marker->flag &= ~MARKER_TRACKED; + copy_v2_v2_int(data->mval, event->mval); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, NULL); break; @@ -1012,9 +947,9 @@ static int clear_track_path_exec(bContext *C, wmOperator *op) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - int action = RNA_enum_get(op->ptr, "action"); + const eTrackClearAction action = RNA_enum_get(op->ptr, "action"); const bool clear_active = RNA_boolean_get(op->ptr, "clear_active"); - int framenr = ED_space_clip_get_clip_frame_number(sc); + const int framenr = ED_space_clip_get_clip_frame_number(sc); if (clear_active) { MovieTrackingTrack *track = BKE_tracking_track_get_active(tracking); diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index ad604fe59bc..bbfbbd2cc58 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -273,7 +273,18 @@ void ed_tracking_deselect_all_plane_tracks(ListBase *plane_tracks_base) } } -static int mouse_select(bContext *C, const float co[2], const bool extend, const bool deselect_all) +static bool select_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + return sc->clip && sc->view == SC_VIEW_CLIP; + } + + return false; +} + +static int select_exec(bContext *C, wmOperator *op) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); @@ -281,12 +292,35 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + + float co[2]; + RNA_float_get_array(op->ptr, "location", co); + + /* Special code which allows to slide a marker which belongs to currently selected but not yet + * active track. If such track is found activate it and return pass-though so that marker slide + * operator can be used immediately after. + * This logic makes it convenient to slide markers when left mouse selection is used. */ + if (!extend) { + MovieTrackingTrack *track = tracking_find_slidable_track_in_proximity(C, co); + if (track != NULL) { + MovieClip *clip_iter = ED_space_clip_get_clip(sc); + + clip_iter->tracking.act_track = track; + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + DEG_id_tag_update(&clip_iter->id, ID_RECALC_SELECT); + + return OPERATOR_PASS_THROUGH; + } + } + float distance_to_track, distance_to_plane_track; - track = find_nearest_track(sc, tracksbase, co, &distance_to_track); - plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track); + MovieTrackingTrack *track = find_nearest_track(sc, tracksbase, co, &distance_to_track); + MovieTrackingPlaneTrack *plane_track = find_nearest_plane_track( + sc, plane_tracks_base, co, &distance_to_plane_track); ClipViewLockState lock_state; ED_clip_view_lock_state_store(C, &lock_state); @@ -375,51 +409,12 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; } -static bool select_poll(bContext *C) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - - if (sc) { - return sc->clip && sc->view == SC_VIEW_CLIP; - } - - return false; -} - -static int select_exec(bContext *C, wmOperator *op) -{ - float co[2]; - - RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - return mouse_select(C, co, extend, deselect_all); -} - static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); ARegion *region = CTX_wm_region(C); float co[2]; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - if (!extend) { - MovieTrackingTrack *track = tracking_marker_check_slide(C, event, NULL, NULL, NULL); - - if (track) { - MovieClip *clip = ED_space_clip_get_clip(sc); - - clip->tracking.act_track = track; - - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); - DEG_id_tag_update(&clip->id, ID_RECALC_SELECT); - - return OPERATOR_PASS_THROUGH; - } - } - ED_clip_mouse_pos(sc, region, event->mval, co); RNA_float_set_array(op->ptr, "location", co); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 59ecef7d4c6..cfbbc1a1b45 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -336,7 +336,7 @@ enum { }; typedef struct FileListEntryPreview { - char path[FILE_MAX]; + char filepath[FILE_MAX]; uint flags; int index; int attributes; /* from FileDirEntry. */ @@ -1636,13 +1636,13 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat source = THB_SOURCE_FONT; } - IMB_thumb_path_lock(preview->path); + IMB_thumb_path_lock(preview->filepath); /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate * in case user switch to a bigger preview size. Do not create preview when file is offline. */ ImBuf *imbuf = (preview->attributes & FILE_ATTR_OFFLINE) ? - IMB_thumb_read(preview->path, THB_LARGE) : - IMB_thumb_manage(preview->path, THB_LARGE, source); - IMB_thumb_path_unlock(preview->path); + IMB_thumb_read(preview->filepath, THB_LARGE) : + IMB_thumb_manage(preview->filepath, THB_LARGE, source); + IMB_thumb_path_unlock(preview->filepath); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); } @@ -1753,7 +1753,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry if (preview_in_memory) { /* TODO(mano-wii): No need to use the thread API here. */ BLI_assert(BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)); - preview->path[0] = '\0'; + preview->filepath[0] = '\0'; ImBuf *imbuf = BKE_previewimg_to_imbuf(preview_in_memory, ICON_SIZE_PREVIEW); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); @@ -1762,13 +1762,13 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry } else { if (entry->redirection_path) { - BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); + BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } else { BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + preview->filepath, sizeof(preview->filepath), filelist->filelist.root, entry->relpath); } - // printf("%s: %d - %s\n", __func__, preview->index, preview->path); + // printf("%s: %d - %s\n", __func__, preview->index, preview->filepath); FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), __func__); @@ -2667,7 +2667,7 @@ bool filelist_cache_previews_update(FileList *filelist) * we do not want to cache it again here. */ entry = filelist_file_ex(filelist, preview->index, false); - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->filepath, preview->img); if (entry) { if (preview->icon_id) { diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 65354591034..10cde239987 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -519,13 +519,13 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx } } -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath) { FSMenuEntry *fsm_iter = NULL; char fsm_name[FILE_MAX]; int nwritten = 0; - FILE *fp = BLI_fopen(filename, "w"); + FILE *fp = BLI_fopen(filepath, "w"); if (!fp) { return; } @@ -556,14 +556,14 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename) fclose(fp); } -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename) +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath) { char line[FILE_MAXDIR]; char name[FILE_MAXFILE]; FSMenuCategory category = FS_CATEGORY_BOOKMARKS; FILE *fp; - fp = BLI_fopen(filename, "r"); + fp = BLI_fopen(filepath, "r"); if (!fp) { return; } diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h index 861c37ecaa1..6e980a326fc 100644 --- a/source/blender/editors/space_file/fsmenu.h +++ b/source/blender/editors/space_file/fsmenu.h @@ -38,10 +38,10 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx); /** saves the 'bookmarks' to the specified file */ -void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename); +void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath); /** reads the 'bookmarks' from the specified file */ -void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filename); +void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath); /** adds system specific directories */ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks); diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index c385420b18e..39fb41245bf 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -61,7 +61,7 @@ if(WITH_IMAGE_CINEON) endif() if(WITH_IMAGE_WEBP) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 68d342e2143..bd1cb3a345e 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1680,184 +1680,28 @@ void IMAGE_OT_replace(wmOperatorType *ot) typedef struct ImageSaveData { ImageUser *iuser; Image *image; - ImageFormatData im_format; + ImageSaveOptions opts; } ImageSaveData; -static char imtype_best_depth(ImBuf *ibuf, const char imtype) +static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOperator *op) { - const char depth_ok = BKE_imtype_valid_depths(imtype); - - if (ibuf->rect_float) { - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - return R_IMF_CHAN_DEPTH_8; - } - - if (depth_ok & R_IMF_CHAN_DEPTH_8) { - return R_IMF_CHAN_DEPTH_8; - } - if (depth_ok & R_IMF_CHAN_DEPTH_12) { - return R_IMF_CHAN_DEPTH_12; - } - if (depth_ok & R_IMF_CHAN_DEPTH_16) { - return R_IMF_CHAN_DEPTH_16; - } - if (depth_ok & R_IMF_CHAN_DEPTH_24) { - return R_IMF_CHAN_DEPTH_24; - } - if (depth_ok & R_IMF_CHAN_DEPTH_32) { - return R_IMF_CHAN_DEPTH_32; - } - return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */ -} - -static int image_save_options_init(Main *bmain, - ImageSaveOptions *opts, - Image *ima, - ImageUser *iuser, - const bool guess_path, - const bool save_as_render) -{ - void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock); - - if (ibuf) { - Scene *scene = opts->scene; - bool is_depth_set = false; - - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { - /* imtype */ - BKE_image_format_init_for_write(&opts->im_format, scene, NULL); - is_depth_set = true; - if (!BKE_image_is_multiview(ima)) { - /* In case multiview is disabled, - * render settings would be invalid for render result in this area. */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - } - } - else { - if (ima->source == IMA_SRC_GENERATED) { - opts->im_format.imtype = R_IMF_IMTYPE_PNG; - opts->im_format.compress = ibuf->foptions.quality; - opts->im_format.planes = ibuf->planes; - } - else { - BKE_image_format_from_imbuf(&opts->im_format, ibuf); - } - - /* use the multiview image settings as the default */ - opts->im_format.stereo3d_format = *ima->stereo3d_format; - opts->im_format.views_format = ima->views_format; - - BKE_image_format_color_management_copy_from_scene(&opts->im_format, scene); - } - - opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; - - if (ima->source == IMA_SRC_TILED) { - BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - } - else { - BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); - } - - /* sanitize all settings */ - - /* unlikely but just in case */ - if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { - opts->im_format.planes = R_IMF_PLANES_RGBA; - } - - /* depth, account for float buffer and format support */ - if (is_depth_set == false) { - opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype); - } - - /* some formats don't use quality so fallback to scenes quality */ - if (opts->im_format.quality == 0) { - opts->im_format.quality = scene->r.im_format.quality; - } - - /* check for empty path */ - if (guess_path && opts->filepath[0] == 0) { - const bool is_prev_save = !STREQ(G.ima, "//"); - if (save_as_render) { - if (is_prev_save) { - BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath)); - } - else { - BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); - } - } - else { - BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2); - BLI_path_make_safe(opts->filepath); - BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); - } - - /* append UDIM marker if not present */ - if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == NULL) { - int len = strlen(opts->filepath); - STR_CONCAT(opts->filepath, len, ".<UDIM>"); - } - } - } - - BKE_image_release_ibuf(ima, ibuf, lock); - - return (ibuf != NULL); -} - -static void image_save_options_from_op(Main *bmain, - ImageSaveOptions *opts, - wmOperator *op, - ImageFormatData *imf) -{ - if (imf) { - BKE_image_format_free(&opts->im_format); - BKE_image_format_copy(&opts->im_format, imf); - } - if (RNA_struct_property_is_set(op->ptr, "filepath")) { RNA_string_get(op->ptr, "filepath", opts->filepath); BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain)); } -} - -static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op) -{ - if (op->customdata) { - ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); - BKE_image_format_copy(&isd->im_format, &opts->im_format); - } - - RNA_string_set(op->ptr, "filepath", opts->filepath); -} -static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) -{ opts->relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); opts->save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); + opts->do_newpath = true; +} +static bool save_image_op( + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) +{ WM_cursor_wait(true); bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts); @@ -1872,11 +1716,54 @@ static bool save_image_op( return ok; } +static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Image *image = image_from_context(C); + ImageUser *iuser = image_user_from_context(C); + Scene *scene = CTX_data_scene(C); + + ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); + isd->image = image; + isd->iuser = iuser; + + if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true, false)) { + BKE_image_save_options_free(&isd->opts); + MEM_freeN(isd); + return NULL; + } + + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + RNA_string_set(op->ptr, "filepath", isd->opts.filepath); + } + + /* Enable save_copy by default for render results. */ + if (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && + !RNA_struct_property_is_set(op->ptr, "copy")) { + RNA_boolean_set(op->ptr, "copy", true); + } + + if (!RNA_struct_property_is_set(op->ptr, "save_as_render")) { + RNA_boolean_set(op->ptr, "save_as_render", isd->opts.save_as_render); + } + + /* Show multiview save options only if image has multiviews. */ + PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(image)); + + op->customdata = isd; + + return isd; +} + static void image_save_as_free(wmOperator *op) { if (op->customdata) { ImageSaveData *isd = op->customdata; - BKE_image_format_free(&isd->im_format); + BKE_image_save_options_free(&isd->opts); MEM_freeN(op->customdata); op->customdata = NULL; @@ -1886,96 +1773,55 @@ static void image_save_as_free(wmOperator *op) static int image_save_as_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; + ImageSaveData *isd; - Image *image = NULL; - ImageUser *iuser = NULL; - ImageFormatData *imf = NULL; if (op->customdata) { - ImageSaveData *isd = op->customdata; - image = isd->image; - iuser = isd->iuser; - imf = &isd->im_format; + isd = op->customdata; } else { - image = image_from_context(C); - iuser = image_user_from_context(C); + isd = image_save_as_init(C, op); + if (isd == NULL) { + return OPERATOR_CANCELLED; + } } - BKE_image_save_options_init(&opts, bmain, scene); - - /* just in case to initialize values, - * these should be set on invoke or by the caller. */ - image_save_options_init(bmain, &opts, image, iuser, false, false); + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); - image_save_options_from_op(bmain, &opts, op, imf); - opts.do_newpath = true; + save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts); - save_image_op(bmain, image, iuser, op, &opts); - - if (opts.save_copy == false) { - BKE_image_free_packedfiles(image); + if (isd->opts.save_copy == false) { + BKE_image_free_packedfiles(isd->image); } - BKE_image_save_options_free(&opts); - image_save_as_free(op); return OPERATOR_FINISHED; } -static bool image_save_as_check(bContext *UNUSED(C), wmOperator *op) +static bool image_save_as_check(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); ImageSaveData *isd = op->customdata; - return WM_operator_filesel_ensure_ext_imtype(op, &isd->im_format); + + image_save_options_from_op(bmain, &isd->opts, op); + BKE_image_save_options_update(&isd->opts, isd->image); + + return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format); } static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - Main *bmain = CTX_data_main(C); - Image *ima = image_from_context(C); - ImageUser *iuser = image_user_from_context(C); - Scene *scene = CTX_data_scene(C); - ImageSaveOptions opts; - PropertyRNA *prop; - const bool save_as_render = (ima->source == IMA_SRC_VIEWER); - if (RNA_struct_property_is_set(op->ptr, "filepath")) { return image_save_as_exec(C, op); } - BKE_image_save_options_init(&opts, bmain, scene); - - if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) { - BKE_image_save_options_free(&opts); + ImageSaveData *isd = image_save_as_init(C, op); + if (isd == NULL) { return OPERATOR_CANCELLED; } - image_save_options_to_op(&opts, op); - /* enable save_copy by default for render results */ - if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && - !RNA_struct_property_is_set(op->ptr, "copy")) { - RNA_boolean_set(op->ptr, "copy", true); - } - - RNA_boolean_set(op->ptr, "save_as_render", save_as_render); - - ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__); - isd->image = ima; - isd->iuser = iuser; - - BKE_image_format_copy(&isd->im_format, &opts.im_format); - op->customdata = isd; - - /* show multiview save options only if image has multiviews */ - prop = RNA_struct_find_property(op->ptr, "show_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - prop = RNA_struct_find_property(op->ptr, "use_multiview"); - RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima)); - - image_filesel(C, op, opts.filepath); - BKE_image_save_options_free(&opts); + image_filesel(C, op, isd->opts.filepath); return OPERATOR_RUNNING_MODAL; } @@ -2003,7 +1849,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) ImageSaveData *isd = op->customdata; PointerRNA imf_ptr; const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); - const bool use_color_management = RNA_boolean_get(op->ptr, "save_as_render"); + const bool save_as_render = RNA_boolean_get(op->ptr, "save_as_render"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -2015,8 +1861,15 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) uiItemS(layout); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr); - uiTemplateImageSettings(layout, &imf_ptr, use_color_management); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->opts.im_format, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, save_as_render); + + if (!save_as_render) { + PointerRNA linear_settings_ptr = RNA_pointer_get(&imf_ptr, "linear_colorspace_settings"); + uiLayout *col = uiLayoutColumn(layout, true); + uiItemS(col); + uiItemR(col, &linear_settings_ptr, "name", 0, IFACE_("Color Space"), ICON_NONE); + } /* multiview template */ if (is_multiview) { @@ -2062,16 +1915,19 @@ void IMAGE_OT_save_as(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, - "save_as_render", - 0, - "Save As Render", - "Apply render part of display transform when saving byte image"); - RNA_def_boolean(ot->srna, - "copy", - 0, - "Copy", - "Create a new image file without modifying the current image in blender"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "save_as_render", + 0, + "Save As Render", + "Apply render part of display transform when saving byte image"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "copy", + 0, + "Copy", + "Create a new image file without modifying the current image in blender"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); image_operator_prop_allow_tokens(ot); WM_operator_properties_filesel(ot, @@ -2138,12 +1994,11 @@ static int image_save_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) { + if (!BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false, false)) { BKE_image_save_options_free(&opts); return OPERATOR_CANCELLED; } - image_save_options_from_op(bmain, &opts, op, NULL); + image_save_options_from_op(bmain, &opts, op); /* Check if file write permission is ok. */ if (BLI_exists(opts.filepath) && !BLI_file_is_writable(opts.filepath)) { @@ -2316,6 +2171,14 @@ static bool image_has_valid_path(Image *ima) return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/'); } +static bool image_should_pack_during_save_all(const Image *ima) +{ + /* Images without a filepath (implied with IMA_SRC_GENERATED) should + * be packed during a save_all operation. */ + return (ima->source == IMA_SRC_GENERATED) || + (ima->source == IMA_SRC_TILED && !BKE_image_has_filepath(ima)); +} + bool ED_image_should_save_modified(const Main *bmain) { ReportList reports; @@ -2339,7 +2202,7 @@ int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { if (!ID_IS_LINKED(ima)) { num_saveable_images++; } @@ -2396,15 +2259,14 @@ bool ED_image_save_all_modified(const bContext *C, ReportList *reports) bool is_format_writable; if (image_should_be_saved(ima, &is_format_writable)) { - if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) { + if (BKE_image_has_packedfile(ima) || image_should_pack_during_save_all(ima)) { BKE_image_memorypack(ima); } else if (is_format_writable) { if (image_has_valid_path(ima)) { ImageSaveOptions opts; Scene *scene = CTX_data_scene(C); - BKE_image_save_options_init(&opts, bmain, scene); - if (image_save_options_init(bmain, &opts, ima, NULL, false, false)) { + if (!BKE_image_save_options_init(&opts, bmain, scene, ima, NULL, false, false)) { bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts); ok = ok && saved_successfully; } @@ -3040,9 +2902,8 @@ static bool image_pack_test(bContext *C, wmOperator *op) return false; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); return false; } @@ -3110,9 +2971,8 @@ static int image_unpack_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -3144,9 +3004,8 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE return OPERATOR_CANCELLED; } - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { - BKE_report( - op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index c3a48abcae1..1fd9fde084b 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -1040,7 +1040,7 @@ static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode) bContext *C = NULL; /* special case, we never read from this. */ UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); ImageUndoStep *us = (ImageUndoStep *)us_p; - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT)); us->paint_mode = paint_mode; return us; } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 568bd064e3e..63f919a1713 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -103,7 +103,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->overlay.flag = SI_OVERLAY_SHOW_OVERLAYS | SI_OVERLAY_SHOW_GRID_BACKGROUND; BKE_imageuser_default(&simage->iuser); - simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS | IMA_SHOW_MAX_RESOLUTION; + simage->iuser.flag = IMA_SHOW_STEREO | IMA_ANIM_ALWAYS; BKE_scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 5e4389279eb..f89bfd2a36a 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -107,6 +107,7 @@ bool nla_panel_context(const bContext *C, found = 1; break; } + case ANIMTYPE_NLAACTION: case ANIMTYPE_SCENE: /* Top-Level Widgets doubling up as datablocks */ case ANIMTYPE_OBJECT: case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 8b059b33a9a..40082b08806 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -58,14 +58,12 @@ * --> Most channels are now selection only. */ -static int mouse_nla_channels( - bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - View2D *v2d = &ac->region->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ @@ -203,47 +201,8 @@ static int mouse_nla_channels( } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; - AnimData *adt = ale->adt; - short offset; - - /* offset for start of channel (on LHS of channel-list) */ - if (ale->id) { - /* special exception for materials and particles */ - if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) { - offset = 21 + NLACHANNEL_BUTTON_WIDTH; - } - else { - offset = 14; - } - } - else { - offset = 0; - } - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - /* toggle protection (only if there's a toggle there) */ - nlt->flag ^= NLATRACK_PROTECTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - } - else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { - /* toggle mute */ - nlt->flag ^= NLATRACK_MUTED; - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { - /* toggle 'solo' */ - BKE_nlatrack_solo_toggle(adt, nlt); - - /* notifier flags - channel was edited */ - notifierFlags |= (ND_ANIMCHAN | NA_EDITED); - ale->update |= ANIM_UPDATE_DEPS; - } - else if (nlaedit_is_tweakmode_on(ac) == 0) { + if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ @@ -269,61 +228,40 @@ static int mouse_nla_channels( case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); - /* button region... */ - if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { - if (nlaedit_is_tweakmode_on(ac) == 0) { - /* 'push-down' action - only usable when not in tweak-mode */ - /* TODO: make this use the operator instead of calling the function directly - * however, calling the operator requires that we supply the args, - * and that works with proper buttons only */ - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ale->id)); - } - else { - /* When in tweak-mode, this button becomes the toggle for mapped editing. */ - adt->flag ^= ADT_NLA_EDIT_NOMAP; - } + /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block + * - this is useful when there's no clear divider, and makes more sense in + * the case of users trying to use this to change actions + * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection + * while in tweak-mode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble + */ + if (nlaedit_is_tweakmode_on(ac)) { + /* Exit tweak-mode immediately. */ + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; ale->update |= ANIM_UPDATE_DEPS; } - /* OR rest of name... */ else { - /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block - * - this is useful when there's no clear divider, and makes more sense in - * the case of users trying to use this to change actions - * - in tweak-mode, clicking here gets us out of tweak-mode, as changing selection - * while in tweak-mode is really evil! - * - we disable "solo" flags too, to make it easier to work with stashed actions - * with less trouble - */ - if (nlaedit_is_tweakmode_on(ac)) { - /* Exit tweak-mode immediately. */ - nlaedit_disable_tweakmode(ac, true); - - /* changes to NLA-Action occurred */ - notifierFlags |= ND_NLA_ACTCHANGE; - ale->update |= ANIM_UPDATE_DEPS; + /* select/deselect */ + if (selectmode == SELECT_INVERT) { + /* inverse selection status of this AnimData block only */ + adt->flag ^= ADT_UI_SELECTED; } else { - /* select/deselect */ - if (selectmode == SELECT_INVERT) { - /* inverse selection status of this AnimData block only */ - adt->flag ^= ADT_UI_SELECTED; - } - else { - /* select AnimData block by itself */ - ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); - adt->flag |= ADT_UI_SELECTED; - } - - /* set active? */ - if (adt->flag & ADT_UI_SELECTED) { - adt->flag |= ADT_UI_ACTIVE; - } + /* select AnimData block by itself */ + ANIM_anim_channels_select_set(ac, ACHANNEL_SETFLAG_CLEAR); + adt->flag |= ADT_UI_SELECTED; + } - notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); + /* set active? */ + if (adt->flag & ADT_UI_SELECTED) { + adt->flag |= ADT_UI_ACTIVE; } + + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } @@ -386,7 +324,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 958a9fdfc60..6806d715004 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -20,6 +20,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "BKE_tracking.h" @@ -213,6 +214,11 @@ static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE); } +static void node_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + NodeResizeDirection node_get_resize_direction(const bNode *node, const int x, const int y) { if (node->type == NODE_FRAME) { @@ -480,6 +486,10 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_MATH: ntype->draw_buttons = node_buts_math; break; + case SH_NODE_COMBINE_COLOR: + case SH_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_buts_combsep_color; + break; case SH_NODE_TEX_IMAGE: ntype->draw_buttons = node_shader_buts_tex_image; ntype->draw_buttons_ex = node_shader_buts_tex_image_ex; @@ -589,6 +599,19 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_composit_buts_combsep_color(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); + if (storage->mode == CMP_NODE_COMBSEP_COLOR_YCC) { + uiItemR(layout, ptr, "ycc_mode", DEFAULT_FLAGS, "", ICON_NONE); + } +} + static void node_composit_backdrop_viewer( SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y) { @@ -821,8 +844,12 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_HUECORRECT: ntype->draw_buttons = node_composit_buts_huecorrect; break; - case CMP_NODE_COMBYCCA: - case CMP_NODE_SEPYCCA: + case CMP_NODE_COMBINE_COLOR: + case CMP_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_composit_buts_combsep_color; + break; + case CMP_NODE_COMBYCCA_LEGACY: + case CMP_NODE_SEPYCCA_LEGACY: ntype->draw_buttons = node_composit_buts_ycc; break; case CMP_NODE_MASK_BOX: @@ -975,6 +1002,11 @@ static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_texture_buts_combsep_color(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); +} + /* only once called */ static void node_texture_set_butfunc(bNodeType *ntype) { @@ -1020,6 +1052,11 @@ static void node_texture_set_butfunc(bNodeType *ntype) case TEX_NODE_OUTPUT: ntype->draw_buttons = node_texture_buts_output; break; + + case TEX_NODE_COMBINE_COLOR: + case TEX_NODE_SEPARATE_COLOR: + ntype->draw_buttons = node_texture_buts_combsep_color; + break; } } } @@ -1235,14 +1272,14 @@ static void node_file_output_socket_draw(bContext *C, static bool socket_needs_attribute_search(bNode &node, bNodeSocket &socket) { - if (node.declaration == nullptr) { + if (node.runtime->declaration == nullptr) { return false; } if (socket.in_out == SOCK_OUT) { return false; } const int socket_index = BLI_findindex(&node.inputs, &socket); - return node.declaration->inputs()[socket_index]->is_attribute_name(); + return node.runtime->declaration->inputs()[socket_index]->is_attribute_name(); } static void std_node_socket_draw( diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 80c5a186021..975d4eda7e3 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -303,10 +303,8 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain, wmOperator *op, bNodeTree *ntree) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - - bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name); + bNodeTree *node_group = reinterpret_cast<bNodeTree *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT)); if (!node_group) { return nullptr; } @@ -423,7 +421,7 @@ void NODE_OT_add_group(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -432,26 +430,16 @@ void NODE_OT_add_group(wmOperatorType *ot) /** \name Add Node Object Operator * \{ */ -static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Object *)BKE_libblock_find_session_uuid(bmain, ID_OB, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Object *)BKE_libblock_find_name(bmain, ID_OB, name); -} - static int node_add_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - Object *object; - if (!(object = node_add_object_get_and_poll_object_node_tree(bmain, op))) { + Object *object = reinterpret_cast<Object *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB)); + + if (!object) { return OPERATOR_CANCELLED; } @@ -508,8 +496,6 @@ static bool node_add_object_poll(bContext *C) void NODE_OT_add_object(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Object"; ot->description = "Add an object info node to the current node editor"; @@ -523,17 +509,7 @@ void NODE_OT_add_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Object", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -542,27 +518,16 @@ void NODE_OT_add_object(wmOperatorType *ot) /** \name Add Node Collection Operator * \{ */ -static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *bmain, - wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); -} - static int node_add_collection_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree *ntree = snode.edittree; - Collection *collection; - if (!(collection = node_add_collection_get_and_poll_collection_node_tree(bmain, op))) { + Collection *collection = reinterpret_cast<Collection *>( + WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); + + if (!collection) { return OPERATOR_CANCELLED; } @@ -619,8 +584,6 @@ static bool node_add_collection_poll(bContext *C) void NODE_OT_add_collection(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Node Collection"; ot->description = "Add an collection info node to the current node editor"; @@ -634,18 +597,7 @@ void NODE_OT_add_collection(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string( - ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -767,7 +719,7 @@ void NODE_OT_add_file(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -776,18 +728,6 @@ void NODE_OT_add_file(wmOperatorType *ot) /** \name Add Mask Node Operator * \{ */ -static ID *node_add_mask_get_and_poll_mask(Main *bmain, wmOperator *op) -{ - if (RNA_struct_property_is_set(op->ptr, "session_uuid")) { - const uint32_t session_uuid = (uint32_t)RNA_int_get(op->ptr, "session_uuid"); - return BKE_libblock_find_session_uuid(bmain, ID_MSK, session_uuid); - } - - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - return BKE_libblock_find_name(bmain, ID_MSK, name); -} - static bool node_add_mask_poll(bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); @@ -801,7 +741,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) SpaceNode &snode = *CTX_wm_space_node(C); bNode *node; - ID *mask = node_add_mask_get_and_poll_mask(bmain, op); + ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK); if (!mask) { return OPERATOR_CANCELLED; } @@ -827,8 +767,6 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) void NODE_OT_add_mask(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Add Mask Node"; ot->description = "Add a mask node to the current node editor"; @@ -841,17 +779,7 @@ void NODE_OT_add_mask(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", "Mask", MAX_ID_NAME - 2, "Name", "Data-block name to assign"); - prop = RNA_def_int(ot->srna, - "session_uuid", - 0, - INT32_MIN, - INT32_MAX, - "Session UUID", - "Session UUID of the data-block to assign", - INT32_MIN, - INT32_MAX); - RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 4247d5a1fbc..b9bee3ed15e 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -26,8 +26,6 @@ #include "UI_interface.hh" #include "UI_resources.h" -#include "UI_interface.hh" - #include "node_intern.hh" struct Curve; @@ -139,7 +137,7 @@ static void get_context_path_node_geometry(const bContext &C, Object *object = CTX_data_active_object(&C); ui::context_path_add_generic(path, RNA_Object, object); ModifierData *modifier = BKE_object_active_modifier(object); - ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_GEOMETRY_NODES); context_path_add_node_tree_and_node_groups(snode, path); } } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 738d482bea3..e0ff4212a94 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -895,9 +895,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), - to_string(mesh_info.tot_verts).c_str(), - to_string(mesh_info.tot_edges).c_str(), - to_string(mesh_info.tot_faces).c_str()); + to_string(mesh_info.verts_num).c_str(), + to_string(mesh_info.edges_num).c_str(), + to_string(mesh_info.faces_num).c_str()); ss << line << line_end; break; } @@ -908,7 +908,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Point Cloud: %s points"), - to_string(pointcloud_info.tot_points).c_str()); + to_string(pointcloud_info.points_num).c_str()); ss << line << line_end; break; } @@ -918,7 +918,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Curve: %s splines"), - to_string(curve_info.tot_splines).c_str()); + to_string(curve_info.splines_num).c_str()); ss << line << line_end; break; } @@ -928,7 +928,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo BLI_snprintf(line, sizeof(line), TIP_("\u2022 Instances: %s"), - to_string(instances_info.tot_instances).c_str()); + to_string(instances_info.instances_num).c_str()); ss << line << line_end; break; } @@ -982,8 +982,8 @@ static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) return true; } - if (socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + if (socket->runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; return !socket_decl.description().is_empty(); } @@ -996,8 +996,8 @@ static char *node_socket_get_tooltip(bContext *C, bNodeSocket *socket) { std::stringstream output; - if (socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + if (socket->runtime->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration; blender::StringRef description = socket_decl.description(); if (!description.is_empty()) { output << TIP_(description.data()); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 2d7972e2291..ab80a44d636 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -66,7 +66,9 @@ namespace blender::ed::space_node { #define USE_ESC_COMPO -/* ***************** composite job manager ********************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Job Manager + * \{ */ enum { COM_RECALC_COMPOSITE = 1, @@ -291,8 +293,14 @@ static void compo_startjob(void *cjv, ntree->progress = nullptr; } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Composite Job C API + * \{ */ + void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner) { using namespace blender::ed::space_node; @@ -336,9 +344,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene WM_jobs_start(CTX_wm_manager(C), wm_job); } +/** \} */ + namespace blender::ed::space_node { -/* ***************************************** */ +/* -------------------------------------------------------------------- */ +/** \name Composite Poll & Utility Functions + * \{ */ bool composite_node_active(bContext *C) { @@ -388,8 +400,14 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) } } +/** \} */ + } // namespace blender::ed::space_node +/* -------------------------------------------------------------------- */ +/** \name Node Editor Public API Functions + * \{ */ + void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree) { if (C != nullptr) { @@ -783,9 +801,13 @@ void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) // node_update_nodetree(C, ntree, 0.0f, 0.0f); } +/** \} */ + namespace blender::ed::space_node { -/* ***************** generic operator functions for nodes ***************** */ +/* -------------------------------------------------------------------- */ +/** \name Generic Operator Functions for Nodes + * \{ */ #if 0 /* UNUSED */ @@ -861,7 +883,11 @@ static void edit_node_properties_get( } #endif -/* ************************** Node generic ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Generic + * \{ */ /* is rct in visible part of node? */ static bNode *visible_node(SpaceNode &snode, const rctf &rct) @@ -874,7 +900,11 @@ static bNode *visible_node(SpaceNode &snode, const rctf &rct) return nullptr; } -/* ********************** size widget operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Size Widget Operator + * \{ */ struct NodeSizeWidget { float mxstart, mystart; @@ -1077,7 +1107,11 @@ void NODE_OT_resize(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING; } -/* ********************** hidden sockets ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hidden Sockets + * \{ */ bool node_has_hidden_sockets(bNode *node) { @@ -1211,7 +1245,11 @@ bool node_find_indicated_socket(SpaceNode &snode, return false; } -/* ****************** Link Dimming *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Link Dimming + * \{ */ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link) { @@ -1237,7 +1275,11 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link) return nodeLinkIsHidden(&link) || node_link_dim_factor(v2d, link) < 0.5f; } -/* ****************** Duplicate *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Duplicate Operator + * \{ */ static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map, bNode *node) @@ -1422,8 +1464,7 @@ void node_select_all(ListBase *lb, int action) } } -/* ******************************** */ -/* XXX some code needing updating to operators. */ +/* XXX: some code needing updating to operators. */ /* goes over all scenes, reads render layers */ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1526,7 +1567,11 @@ void NODE_OT_render_changed(wmOperatorType *ot) ot->flag = 0; } -/* ****************** Hide operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Hide Operator + * \{ */ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag) { @@ -1722,7 +1767,11 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Mute operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Mute Operator + * \{ */ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1758,7 +1807,11 @@ void NODE_OT_mute_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete operator ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete Operator + * \{ */ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1793,7 +1846,11 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Switch View ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Switch View + * \{ */ static bool node_switch_view_poll(bContext *C) { @@ -1837,7 +1894,12 @@ void NODE_OT_switch_view_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Delete with reconnect ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Delete with Reconnect Operator + * \{ */ + static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1872,7 +1934,11 @@ void NODE_OT_delete_reconnect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** File Output Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node File Output Add Socket Operator + * \{ */ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op) { @@ -1922,7 +1988,11 @@ void NODE_OT_output_file_add_socket(wmOperatorType *ot) ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Subpath of the output file"); } -/* ****************** Multi File Output Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Remove Socket Operator + * \{ */ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1968,7 +2038,11 @@ void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Multi File Output Move Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Multi File Output Move Socket Node + * \{ */ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op) { @@ -2040,7 +2114,11 @@ void NODE_OT_output_file_move_active_socket(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", ""); } -/* ****************** Copy Node Color ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy Node Color Operator + * \{ */ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2085,7 +2163,11 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Copy to clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Copy to Clipboard Operator + * \{ */ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2163,7 +2245,11 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Paste from clipboard ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Paste from Clipboard + * \{ */ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) { @@ -2287,7 +2373,11 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** Add interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Add Interface Socket Operator + * \{ */ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb) { @@ -2357,7 +2447,11 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Remove interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Remove Interface Socket Operator + * \{ */ static int ntree_socket_remove_exec(bContext *C, wmOperator *op) { @@ -2403,7 +2497,11 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/********************** Change interface socket type operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Change Interface Socket Type Operator + * \{ */ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) { @@ -2503,7 +2601,11 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot) ot->prop = prop; } -/********************** Move interface socket operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node-Tree Move Interface Socket Operator + * \{ */ static const EnumPropertyItem move_direction_items[] = { {1, "UP", 0, "Up", ""}, @@ -2577,7 +2679,11 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); } -/* ********************** Shader Script Update ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Shader Script Update + * \{ */ static bool node_shader_script_update_poll(bContext *C) { @@ -2722,7 +2828,11 @@ void NODE_OT_shader_script_update(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ********************** Viewer border ******************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Node Viewer Border + * \{ */ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, @@ -2844,7 +2954,11 @@ void NODE_OT_clear_viewer_border(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Add Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Add Socket + * \{ */ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2888,7 +3002,11 @@ void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Cryptomatte Remove Socket ******************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Remove Socket + * \{ */ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2933,4 +3051,7 @@ void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/** \} */ + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 9c0172cfabf..ed981603293 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -82,8 +82,10 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context( if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { - if (names.add(attribute.name)) { - attributes.append(&attribute); + if (bke::allow_procedural_attribute_access(attribute.name)) { + if (names.add(attribute.name)) { + attributes.append(&attribute); + } } } } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 6d7348bfffc..924537d0e8a 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -31,7 +31,9 @@ struct wmKeyConfig; struct wmWindow; /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { extern const char *node_context_dir[]; +}; namespace blender::ed::space_node { @@ -74,13 +76,17 @@ struct SpaceNode_Runtime { /** Mouse position for drawing socket-less links and adding nodes. */ float2 cursor; - /* Indicates that the compositing tree in the space needs to be re-evaluated using the + /** + * Indicates that the compositing tree in the space needs to be re-evaluated using the * auto-compositing pipeline. - * Takes priority over the regular compsiting. */ + * Takes priority over the regular compositing. + */ bool recalc_auto_compositing; - /* Indicates that the compositing int the space tree needs to be re-evaluated using - * regular compositing pipeline. */ + /** + * Indicates that the compositing int the space tree needs to be re-evaluated using + * regular compositing pipeline. + */ bool recalc_regular_compositing; /** Temporary data for modal linking operator. */ @@ -100,7 +106,7 @@ enum NodeResizeDirection { }; ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); -/* Nodes draw without dpi - the view zoom is flexible. */ +/* Nodes draw without DPI - the view zoom is flexible. */ #define HIDDEN_RAD (0.75f * U.widget_unit) #define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index c757fb46407..5796a712205 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2048,7 +2048,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn /* Try to get the main socket based on the socket declaration. */ nodeDeclarationEnsure(&ntree, &node); - const nodes::NodeDeclaration *node_decl = node.declaration; + const nodes::NodeDeclaration *node_decl = node.runtime->declaration; if (node_decl != nullptr) { Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() : node_decl->outputs(); diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 005dbc1eb10..9d73156edab 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -716,7 +716,7 @@ void NODE_OT_select(wmOperatorType *ot) prop = RNA_def_int_vector(ot->srna, "location", 2, - NULL, + nullptr, INT_MIN, INT_MAX, "Location", diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 296cd1ff133..15afd024766 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -327,8 +327,9 @@ static bool any_node_uses_id(const bNodeTree *ntree, const ID *id) /** * Tag the space to recalculate the compositing tree using auto-compositing pipeline. * - * Will check the space to be using a compsiting tree, and check whether auto-compositing - * is enabled. If the checks do not pass then the function has no affect. */ + * Will check the space to be using a compositing tree, and check whether auto-compositing + * is enabled. If the checks do not pass then the function has no affect. + */ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *area) { if (!ED_node_is_compositor(snode)) { @@ -347,7 +348,8 @@ static void node_area_tag_recalc_auto_compositing(SpaceNode *snode, ScrArea *are * For all node trees this will do `snode_set_context()` which takes care of setting an active * tree. This will be done in the area refresh callback. * - * For compositor tree this will additionally start of the compositor job. */ + * For compositor tree this will additionally start of the compositor job. + */ static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area) { if (ED_node_is_compositor(snode)) { @@ -670,7 +672,7 @@ static void node_group_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *d { ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); } static void node_id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) @@ -685,7 +687,7 @@ static void node_id_path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { - RNA_string_set(drop->ptr, "name", id->name + 2); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); RNA_struct_property_unset(drop->ptr, "filepath"); } else if (drag->path[0]) { @@ -824,8 +826,10 @@ static void node_region_listener(const wmRegionListenerParams *params) } // namespace blender::ed::space_node /* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */ +extern "C" { const char *node_context_dir[] = { "selected_nodes", "active_node", "light", "material", "world", nullptr}; +}; namespace blender::ed::space_node { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index fae0e4be2a8..97d2957eed2 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC outliner_draw.cc outliner_edit.cc outliner_ops.cc + outliner_query.cc outliner_select.cc outliner_sync.cc outliner_tools.cc @@ -38,8 +39,8 @@ set(SRC tree/tree_display_data.cc tree/tree_display_libraries.cc tree/tree_display_orphaned.cc - tree/tree_display_override_library_properties.cc tree/tree_display_override_library_hierarchies.cc + tree/tree_display_override_library_properties.cc tree/tree_display_scenes.cc tree/tree_display_sequencer.cc tree/tree_display_view_layer.cc @@ -57,6 +58,7 @@ set(SRC tree/tree_element_scene_objects.cc tree/tree_element_seq.cc tree/tree_element_view_layer.cc + tree/tree_iterator.cc outliner_intern.hh tree/common.hh @@ -75,6 +77,7 @@ set(SRC tree/tree_element_scene_objects.hh tree/tree_element_seq.hh tree/tree_element_view_layer.hh + tree/tree_iterator.hh ) set(LIB diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc index d07b6641836..1a804cb58b8 100644 --- a/source/blender/editors/space_outliner/outliner_context.cc +++ b/source/blender/editors/space_outliner/outliner_context.cc @@ -12,23 +12,25 @@ #include "DNA_space_types.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" -static void outliner_context_selected_ids_recursive(const ListBase *subtree, +using namespace blender::ed::outliner; + +static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner, bContextDataResult *result) { - LISTBASE_FOREACH (const TreeElement *, te, subtree) { + tree_iterator::all(space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tse = TREESTORE(te); if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))) { CTX_data_id_list_add(result, tse->id); } - outliner_context_selected_ids_recursive(&te->subtree, result); - } + }); } static void outliner_context_selected_ids(const SpaceOutliner *space_outliner, bContextDataResult *result) { - outliner_context_selected_ids_recursive(&space_outliner->tree, result); + outliner_context_selected_ids_recursive(*space_outliner, result); CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 88640210ea3..e20958c1b1e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -170,7 +170,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C, *r_insert_type = TE_INSERT_BEFORE; return first; } - BLI_assert(0); + + BLI_assert_unreachable(); return nullptr; } @@ -315,7 +316,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); - bool changed = outliner_flag_set(&space_outliner->tree, TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_DRAG_ANY, false); if (changed) { ED_region_tag_redraw_no_rebuild(CTX_wm_region(C)); } @@ -846,8 +847,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); if (!drop_data) { @@ -1194,8 +1194,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); - bool changed = outliner_flag_set( - &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; if (((event->modifier & KM_SHIFT) == 0) && @@ -1460,7 +1459,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, /* Only drag element under mouse if it was not selected before. */ if ((tselem->flag & TSE_SELECTED) == 0) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); tselem->flag |= TSE_SELECTED; } diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 36e21cf51a5..753de83a10d 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -38,6 +38,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_report.h" @@ -70,7 +71,9 @@ #include "tree/tree_element_id.hh" #include "tree/tree_element_overrides.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" +using namespace blender; using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ @@ -1713,72 +1716,70 @@ static void outliner_draw_restrictbuts(uiBlock *block, } static void outliner_draw_userbuts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb) + const ARegion *region, + const SpaceOutliner *space_outliner) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } - LISTBASE_FOREACH (TreeElement *, te, lb) { - TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (tselem->type == TSE_SOME_ID) { - uiBut *bt; - ID *id = tselem->id; - const char *tip = nullptr; - char buf[16] = ""; - int but_flag = UI_BUT_DRAG_LOCK; + const TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type != TSE_SOME_ID) { + return; + } - if (ID_IS_LINKED(id)) { - but_flag |= UI_BUT_DISABLED; - } + uiBut *bt; + ID *id = tselem->id; + const char *tip = nullptr; + char buf[16] = ""; + int but_flag = UI_BUT_DRAG_LOCK; - BLI_str_format_int_grouped(buf, id->us); - bt = uiDefBut(block, - UI_BTYPE_BUT, - 1, - buf, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0, - 0, - TIP_("Number of users of this data-block")); - UI_but_flag_enable(bt, but_flag); - - if (id->flag & LIB_FAKEUSER) { - tip = TIP_("Data-block will be retained using a fake user"); - } - else { - tip = TIP_("Data-block has no users and will be deleted"); - } - bt = uiDefIconButBitS(block, - UI_BTYPE_ICON_TOGGLE, - LIB_FAKEUSER, - 1, - ICON_FAKE_USER_OFF, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - &id->flag, - 0, - 0, - 0, - 0, - tip); - UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); - UI_but_flag_enable(bt, but_flag); - } + if (ID_IS_LINKED(id)) { + but_flag |= UI_BUT_DISABLED; } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_userbuts(block, region, space_outliner, &te->subtree); + BLI_str_format_int_grouped(buf, id->us); + bt = uiDefBut(block, + UI_BTYPE_BUT, + 1, + buf, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0, + 0, + TIP_("Number of users of this data-block")); + UI_but_flag_enable(bt, but_flag); + + if (id->flag & LIB_FAKEUSER) { + tip = TIP_("Data-block will be retained using a fake user"); } - } + else { + tip = TIP_("Data-block has no users and will be deleted"); + } + bt = uiDefIconButBitS(block, + UI_BTYPE_ICON_TOGGLE, + LIB_FAKEUSER, + 1, + ICON_FAKE_USER_OFF, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + &id->flag, + 0, + 0, + 0, + 0, + tip); + UI_but_func_set(bt, restrictbutton_id_user_toggle, id, nullptr); + UI_but_flag_enable(bt, but_flag); + }); } static void outliner_draw_overrides_rna_buts(uiBlock *block, @@ -1817,7 +1818,7 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, te->ys + pad_y, item_max_width, item_height, - NULL, + nullptr, 0.0f, 0.0f, 0.0f, @@ -1919,91 +1920,6 @@ static void outliner_draw_overrides_restrictbuts(Main *bmain, } } -static bool outliner_draw_overrides_warning_buts(uiBlock *block, - ARegion *region, - SpaceOutliner *space_outliner, - ListBase *lb, - const bool is_open) -{ - bool any_item_has_warnings = false; - - LISTBASE_FOREACH (TreeElement *, te, lb) { - bool item_has_warnings = false; - const bool do_draw = outliner_is_element_in_view(te, ®ion->v2d); - int but_flag = UI_BUT_DRAG_LOCK; - const char *tip = nullptr; - - TreeStoreElem *tselem = TREESTORE(te); - switch (tselem->type) { - case TSE_LIBRARY_OVERRIDE_BASE: { - ID *id = tselem->id; - - if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override data-block is not needed anymore, but was detected as user-edited"); - } - } - else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_("This override data-block is unused"); - } - } - break; - } - case TSE_LIBRARY_OVERRIDE: { - TreeElementOverridesProperty &te_override_prop = - *tree_element_cast<TreeElementOverridesProperty>(te); - if (!te_override_prop.is_rna_path_valid) { - item_has_warnings = true; - if (do_draw) { - tip = TIP_( - "This override property does not exist in current data, it will be removed on " - "next .blend file save"); - } - } - break; - } - default: - break; - } - - const bool any_child_has_warnings = outliner_draw_overrides_warning_buts( - block, - region, - space_outliner, - &te->subtree, - is_open && TSELEM_OPEN(tselem, space_outliner)); - - if (do_draw && - (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) { - if (tip == nullptr) { - tip = TIP_("Some sub-items require attention"); - } - uiBut *bt = uiDefIconBut(block, - UI_BTYPE_BUT, - 1, - ICON_ERROR, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0.0, - 0.0, - tip); - UI_but_flag_enable(bt, but_flag); - } - any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; - } - - return any_item_has_warnings; -} - static void outliner_draw_separator(ARegion *region, const int x) { View2D *v2d = ®ion->v2d; @@ -2024,81 +1940,82 @@ static void outliner_draw_separator(ARegion *region, const int x) immUnbindProgram(); } -static void outliner_draw_rnabuts( - uiBlock *block, ARegion *region, SpaceOutliner *space_outliner, int sizex, ListBase *lb) +static void outliner_draw_rnabuts(uiBlock *block, + ARegion *region, + SpaceOutliner *space_outliner, + int sizex) { PointerRNA ptr; PropertyRNA *prop; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - if (outliner_is_element_in_view(te, ®ion->v2d)) { - if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { - ptr = te_rna_prop->getPointerRNA(); - prop = te_rna_prop->getPropertyRNA(); - - if (!TSELEM_OPEN(tselem, space_outliner)) { - if (RNA_property_type(prop) == PROP_POINTER) { - uiBut *but = uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else if (RNA_property_type(prop) == PROP_ENUM) { - uiDefAutoButR(block, - &ptr, - prop, - -1, - nullptr, - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - else { - uiDefAutoButR(block, - &ptr, - prop, - -1, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - } - else if (TreeElementRNAArrayElement *te_rna_array_elem = - tree_element_cast<TreeElementRNAArrayElement>(te)) { - ptr = te_rna_array_elem->getPointerRNA(); - prop = te_rna_array_elem->getPropertyRNA(); - - uiDefAutoButR(block, - &ptr, - prop, - te->index, - "", - ICON_NONE, - sizex, - te->ys, - OL_RNA_COL_SIZEX, - UI_UNIT_Y - 1); - } - } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_rnabuts(block, region, space_outliner, sizex, &te->subtree); - } - } + if (!outliner_is_element_in_view(te, ®ion->v2d)) { + return; + } + + if (TreeElementRNAProperty *te_rna_prop = tree_element_cast<TreeElementRNAProperty>(te)) { + ptr = te_rna_prop->getPointerRNA(); + prop = te_rna_prop->getPropertyRNA(); + + if (!TSELEM_OPEN(tselem, space_outliner)) { + if (RNA_property_type(prop) == PROP_POINTER) { + uiBut *but = uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else if (RNA_property_type(prop) == PROP_ENUM) { + uiDefAutoButR(block, + &ptr, + prop, + -1, + nullptr, + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + else { + uiDefAutoButR(block, + &ptr, + prop, + -1, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + } + } + else if (TreeElementRNAArrayElement *te_rna_array_elem = + tree_element_cast<TreeElementRNAArrayElement>(te)) { + ptr = te_rna_array_elem->getPointerRNA(); + prop = te_rna_array_elem->getPropertyRNA(); + + uiDefAutoButR(block, + &ptr, + prop, + te->index, + "", + ICON_NONE, + sizex, + te->ys, + OL_RNA_COL_SIZEX, + UI_UNIT_Y - 1); + } + }); } static void outliner_buttons(const bContext *C, @@ -2184,9 +2101,9 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED static void outliner_draw_mode_column_toggle(uiBlock *block, TreeViewContext *tvc, TreeElement *te, - TreeStoreElem *tselem, const bool lock_object_modes) { + TreeStoreElem *tselem = TREESTORE(te); if ((tselem->type != TSE_SOME_ID) || (te->idcode != ID_OB)) { return; } @@ -2257,59 +2174,63 @@ static void outliner_draw_mode_column_toggle(uiBlock *block, } } -static void outliner_draw_mode_column(const bContext *C, - uiBlock *block, +static void outliner_draw_mode_column(uiBlock *block, TreeViewContext *tvc, - SpaceOutliner *space_outliner, - ListBase *tree) + SpaceOutliner *space_outliner) { - TreeStoreElem *tselem; const bool lock_object_modes = tvc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK; - LISTBASE_FOREACH (TreeElement *, te, tree) { - tselem = TREESTORE(te); - + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { if (tvc->obact && tvc->obact->mode != OB_MODE_OBJECT) { - outliner_draw_mode_column_toggle(block, tvc, te, tselem, lock_object_modes); + outliner_draw_mode_column_toggle(block, tvc, te, lock_object_modes); } + }); +} - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_mode_column(C, block, tvc, space_outliner, &te->subtree); +static StringRefNull outliner_draw_get_warning_tree_element_subtree(const TreeElement *parent_te) +{ + LISTBASE_FOREACH (const TreeElement *, sub_te, &parent_te->subtree) { + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(sub_te); + StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; + + if (!warning_msg.is_empty()) { + return warning_msg; + } + + warning_msg = outliner_draw_get_warning_tree_element_subtree(sub_te); + if (!warning_msg.is_empty()) { + return warning_msg; } } + + return ""; } -/* Returns `true` if some warning was drawn for that element or one of its sub-elements (if it is - * not open). */ -static bool outliner_draw_warning_tree_element(uiBlock *block, - SpaceOutliner *space_outliner, - TreeElement *te, - TreeStoreElem *tselem, - const bool use_mode_column, - const int te_ys) +static StringRefNull outliner_draw_get_warning_tree_element(const SpaceOutliner &space_outliner, + const TreeElement *te) { - if ((te->flag & TE_HAS_WARNING) == 0) { - /* If given element has no warning, recursively try to display the first sub-elements' warning. - */ - if (!TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, sub_te, &te->subtree) { - TreeStoreElem *sub_tselem = TREESTORE(sub_te); + const AbstractTreeElement *abstract_te = tree_element_cast<AbstractTreeElement>(te); + const StringRefNull warning_msg = abstract_te ? abstract_te->getWarning() : ""; - if (outliner_draw_warning_tree_element( - block, space_outliner, sub_te, sub_tselem, use_mode_column, te_ys)) { - return true; - } - } - } - return false; + if (!warning_msg.is_empty()) { + return warning_msg; + } + + /* If given element has no warning, recursively try to display the first sub-element's warning. + */ + if (!TSELEM_OPEN(te->store_elem, &space_outliner)) { + return outliner_draw_get_warning_tree_element_subtree(te); } - int icon = ICON_NONE; - const char *tip = ""; - const bool has_warning = tree_element_warnings_get(te, &icon, &tip); - BLI_assert(has_warning); - UNUSED_VARS_NDEBUG(has_warning); + return ""; +} +static void outliner_draw_warning_tree_element(uiBlock *block, + const SpaceOutliner *space_outliner, + StringRefNull warning_msg, + const bool use_mode_column, + const int te_ys) +{ /* Move the warnings a unit left in view layer mode. */ const short mode_column_offset = (use_mode_column && (space_outliner->outlinevis == SO_SCENES)) ? UI_UNIT_X : @@ -2319,7 +2240,7 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, uiBut *but = uiDefIconBut(block, UI_BTYPE_ICON_TOGGLE, 0, - icon, + ICON_ERROR, mode_column_offset, te_ys, UI_UNIT_X, @@ -2329,28 +2250,25 @@ static bool outliner_draw_warning_tree_element(uiBlock *block, 0.0, 0.0, 0.0, - tip); + warning_msg.c_str()); /* No need for undo here, this is a pure info widget. */ UI_but_flag_disable(but, UI_BUT_UNDO); - - return true; } -static void outliner_draw_warning_column(const bContext *C, - uiBlock *block, - SpaceOutliner *space_outliner, - const bool use_mode_column, - ListBase *tree) +static void outliner_draw_warning_column(uiBlock *block, + const SpaceOutliner *space_outliner, + const bool use_mode_column) { - LISTBASE_FOREACH (TreeElement *, te, tree) { - TreeStoreElem *tselem = TREESTORE(te); + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { + /* Get warning for this element, or if there is none and the element is collapsed, the first + * warning in the collapsed sub-tree. */ + StringRefNull warning_msg = outliner_draw_get_warning_tree_element(*space_outliner, te); - outliner_draw_warning_tree_element(block, space_outliner, te, tselem, use_mode_column, te->ys); - - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &te->subtree); + if (!warning_msg.is_empty()) { + outliner_draw_warning_tree_element( + block, space_outliner, warning_msg, use_mode_column, te->ys); } - } + }); } /** \} */ @@ -2524,6 +2442,11 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) return ICON_WORKSPACE; case ID_MSK: return ICON_MOD_MASK; + case ID_NT: { + const bNodeTree *ntree = (bNodeTree *)id; + const bNodeTreeType *ntreetype = ntree->typeinfo; + return (BIFIconID)ntreetype->ui_icon; + } case ID_MC: return ICON_SEQUENCE; case ID_PC: @@ -3226,18 +3149,16 @@ static void outliner_draw_iconrow(bContext *C, } /* closed tree element */ -static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty) +static void outliner_set_subtree_coords(const TreeElement *te) { - /* closed items may be displayed in row of parent, don't change their coordinate! */ - if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { - te->xs = 0; - te->ys = 0; - te->xend = 0; - } - - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty); - } + tree_iterator::all(te->subtree, [&](TreeElement *te) { + /* closed items may be displayed in row of parent, don't change their coordinate! */ + if ((te->flag & TE_ICONROW) == 0 && (te->flag & TE_ICONROW_MERGED) == 0) { + te->xs = 0; + te->ys = 0; + te->xend = 0; + } + }); } static bool element_should_draw_faded(const TreeViewContext *tvc, @@ -3489,10 +3410,7 @@ static void outliner_draw_tree_element(bContext *C, } } else { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coord_tree_element(ten, startx, *starty); - } - + outliner_set_subtree_coords(te); *starty -= UI_UNIT_Y; } } @@ -3648,22 +3566,21 @@ static void outliner_draw_struct_marks(ARegion *region, } } -static void outliner_draw_highlights_recursive(uint pos, - const ARegion *region, - const SpaceOutliner *space_outliner, - const ListBase *lb, - const float col_selection[4], - const float col_active[4], - const float col_highlight[4], - const float col_searchmatch[4], - int start_x, - int *io_start_y) +static void outliner_draw_highlights(uint pos, + const ARegion *region, + const SpaceOutliner *space_outliner, + const float col_selection[4], + const float col_active[4], + const float col_highlight[4], + const float col_searchmatch[4], + int start_x, + int *io_start_y) { const bool is_searching = (SEARCHING_OUTLINER(space_outliner) || (space_outliner->outlinevis == SO_DATA_API && space_outliner->search_string[0] != 0)); - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](const TreeElement *te) { const TreeStoreElem *tselem = TREESTORE(te); const int start_y = *io_start_y; @@ -3719,19 +3636,7 @@ static void outliner_draw_highlights_recursive(uint pos, } *io_start_y -= UI_UNIT_Y; - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_draw_highlights_recursive(pos, - region, - space_outliner, - &te->subtree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - start_x + UI_UNIT_X, - io_start_y); - } - } + }); } static void outliner_draw_highlights(ARegion *region, @@ -3753,16 +3658,15 @@ static void outliner_draw_highlights(ARegion *region, GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - outliner_draw_highlights_recursive(pos, - region, - space_outliner, - &space_outliner->tree, - col_selection, - col_active, - col_highlight, - col_searchmatch, - startx, - starty); + outliner_draw_highlights(pos, + region, + space_outliner, + col_selection, + col_active, + col_highlight, + col_searchmatch, + startx, + starty); immUnbindProgram(); GPU_blend(GPU_BLEND_NONE); } @@ -3955,13 +3859,8 @@ void draw_outliner(const bContext *C) UI_view2d_view_ortho(v2d); /* Only show mode column in View Layers and Scenes view. */ - const bool use_mode_column = (space_outliner->flag & SO_MODE_COLUMN) && - (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)); - - const bool use_warning_column = ELEM(space_outliner->outlinevis, - SO_LIBRARIES, - SO_OVERRIDES_LIBRARY) && - space_outliner->runtime->tree_display->hasWarnings(); + const bool use_mode_column = outliner_shows_mode_column(*space_outliner); + const bool use_warning_column = outliner_has_element_warnings(*space_outliner); /* Draw outliner stuff (background, hierarchy lines and names). */ const float right_column_width = outliner_right_columns_width(space_outliner); @@ -3991,18 +3890,14 @@ void draw_outliner(const bContext *C) outliner_draw_separator(region, buttons_start_x + OL_RNA_COL_SIZEX); UI_block_emboss_set(block, UI_EMBOSS); - outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x, &space_outliner->tree); + outliner_draw_rnabuts(block, region, space_outliner, buttons_start_x); UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); } else if (space_outliner->outlinevis == SO_ID_ORPHANS) { /* draw user toggle columns */ - outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree); + outliner_draw_userbuts(block, region, space_outliner); } else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) { - /* Draw overrides status columns. */ - outliner_draw_overrides_warning_buts( - block, region, space_outliner, &space_outliner->tree, true); - const int x = region->v2d.cur.xmax - right_column_width; outliner_draw_separator(region, x); if (space_outliner->lib_override_view_mode == SO_LIB_OVERRIDE_VIEW_PROPERTIES) { @@ -4031,12 +3926,12 @@ void draw_outliner(const bContext *C) /* Draw mode icons */ if (use_mode_column) { - outliner_draw_mode_column(C, block, &tvc, space_outliner, &space_outliner->tree); + outliner_draw_mode_column(block, &tvc, space_outliner); } /* Draw warning icons */ if (use_warning_column) { - outliner_draw_warning_column(C, block, space_outliner, use_mode_column, &space_outliner->tree); + outliner_draw_warning_column(block, space_outliner, use_mode_column); } UI_block_emboss_set(block, UI_EMBOSS); diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index d6c5901b546..c4a9398a5f7 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -60,6 +60,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -107,7 +108,7 @@ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const if (!hovered_te || !is_over_icon || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED) || !(icon_te->store_elem->flag & TSE_HIGHLIGHTED_ICON)) { /* Clear highlights when nothing is hovered or when a new item is hovered. */ - changed = outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); + changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); if (hovered_te) { hovered_te->store_elem->flag |= TSE_HIGHLIGHTED; changed = true; @@ -167,7 +168,7 @@ void outliner_item_openclose(SpaceOutliner *space_outliner, } if (toggle_all) { - outliner_flag_set(&te->subtree, TSE_CLOSED, !open); + outliner_flag_set(te->subtree, TSE_CLOSED, !open); } } @@ -334,8 +335,16 @@ static void do_item_rename(ARegion *region, add_textbut = true; } } - else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { - BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + else if (te->idcode == ID_LI) { + if (reinterpret_cast<Library *>(tselem->id)->parent) { + BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); + } + else { + BKE_report( + reports, + RPT_WARNING, + "Library path is not editable from here anymore, please use Relocate operation instead"); + } } else { add_textbut = true; @@ -1069,11 +1078,16 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel) return 0; } -bool outliner_flag_set(ListBase *lb, short flag, short set) +bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set) +{ + return outliner_flag_set(space_outliner.tree, flag, set); +} + +bool outliner_flag_set(const ListBase &lb, const short flag, const short set) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); bool has_flag = (tselem->flag & flag); if (set == 0) { @@ -1086,21 +1100,24 @@ bool outliner_flag_set(ListBase *lb, short flag, short set) tselem->flag |= flag; changed = true; } - changed |= outliner_flag_set(&te->subtree, flag, set); - } + }); return changed; } -bool outliner_flag_flip(ListBase *lb, short flag) +bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag) +{ + return outliner_flag_flip(space_outliner.tree, flag); +} + +bool outliner_flag_flip(const ListBase &lb, const short flag) { bool changed = false; - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all(lb, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); tselem->flag ^= flag; - changed |= outliner_flag_flip(&te->subtree, flag); - } + }); return changed; } @@ -1117,10 +1134,10 @@ static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op)) ARegion *region = CTX_wm_region(C); if (outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1)) { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 0); + outliner_flag_set(*space_outliner, TSE_CLOSED, 0); } else { - outliner_flag_set(&space_outliner->tree, TSE_CLOSED, 1); + outliner_flag_set(*space_outliner, TSE_CLOSED, 1); } ED_region_tag_redraw(region); @@ -1161,13 +1178,13 @@ static int outliner_select_all_exec(bContext *C, wmOperator *op) switch (action) { case SEL_SELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 1); + outliner_flag_set(*space_outliner, TSE_SELECTED, 1); break; case SEL_DESELECT: - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); break; case SEL_INVERT: - outliner_flag_flip(&space_outliner->tree, TSE_SELECTED); + outliner_flag_flip(*space_outliner, TSE_SELECTED); break; } @@ -1203,32 +1220,16 @@ void OUTLINER_OT_select_all(wmOperatorType *ot) /** \name View Show Active (Outliner) Operator * \{ */ -static void outliner_set_coordinates_element_recursive(SpaceOutliner *space_outliner, - TreeElement *te, - int startx, - int *starty) -{ - TreeStoreElem *tselem = TREESTORE(te); - - /* store coord and continue, we need coordinates for elements outside view too */ - te->xs = (float)startx; - te->ys = (float)(*starty); - *starty -= UI_UNIT_Y; - - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) { - outliner_set_coordinates_element_recursive(space_outliner, ten, startx + UI_UNIT_X, starty); - } - } -} - -void outliner_set_coordinates(ARegion *region, SpaceOutliner *space_outliner) +void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner) { int starty = (int)(region->v2d.tot.ymax) - UI_UNIT_Y; - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_set_coordinates_element_recursive(space_outliner, te, 0, &starty); - } + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + /* store coord and continue, we need coordinates for elements outside view too */ + te->xs = 0; + te->ys = (float)starty; + starty -= UI_UNIT_Y; + }); } /* return 1 when levels were opened */ @@ -1616,11 +1617,11 @@ static int subtree_has_objects(ListBase *lb) return 0; } -/* recursive helper function for Show Hierarchy operator */ -static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner, ListBase *lb) +/* Helper function for Show Hierarchy operator */ +static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner) { /* open all object elems, close others */ - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (ELEM(tselem->type, @@ -1648,11 +1649,7 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outli else { tselem->flag |= TSE_CLOSED; } - - if (TSELEM_OPEN(tselem, space_outliner)) { - tree_element_show_hierarchy(scene, space_outliner, &te->subtree); - } - } + }); } /* show entire object level hierarchy */ @@ -1663,7 +1660,7 @@ static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); /* recursively open/close levels */ - tree_element_show_hierarchy(scene, space_outliner, &space_outliner->tree); + tree_element_show_hierarchy(scene, space_outliner); ED_region_tag_redraw(region); @@ -1865,79 +1862,75 @@ enum { DRIVERS_EDITMODE_REMOVE, } /*eDrivers_EditModes*/; -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, - ListBase *tree, ReportList *reports, short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; - PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - - /* check if RNA-property described by this selected element is an animatable prop */ - if (prop && RNA_property_animateable(&ptr, prop)) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; - int arraylen = 1; + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; - /* array checks */ - if (flag & KSP_FLAG_WHOLE_ARRAY) { - /* entire array was selected, so add drivers for all */ - arraylen = RNA_property_array_length(&ptr, prop); - } - else { - arraylen = array_index; - } + TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna ? te_rna->getPointerRNA() : PointerRNA_NULL; + PropertyRNA *prop = te_rna ? te_rna->getPropertyRNA() : nullptr; - /* we should do at least one step */ - if (arraylen == array_index) { - arraylen++; - } + /* check if RNA-property described by this selected element is an animatable prop */ + if (prop && RNA_property_animateable(&ptr, prop)) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - /* for each array element we should affect, add driver */ - for (; array_index < arraylen; array_index++) { - /* action depends on mode */ - switch (mode) { - case DRIVERS_EDITMODE_ADD: { - /* add a new driver with the information obtained (only if valid) */ - ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); - break; - } - case DRIVERS_EDITMODE_REMOVE: { - /* remove driver matching the information obtained (only if valid) */ - ANIM_remove_driver(reports, id, path, array_index, dflags); - break; - } + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR; + int arraylen = 1; + + /* array checks */ + if (flag & KSP_FLAG_WHOLE_ARRAY) { + /* entire array was selected, so add drivers for all */ + arraylen = RNA_property_array_length(&ptr, prop); + } + else { + arraylen = array_index; + } + + /* we should do at least one step */ + if (arraylen == array_index) { + arraylen++; + } + + /* for each array element we should affect, add driver */ + for (; array_index < arraylen; array_index++) { + /* action depends on mode */ + switch (mode) { + case DRIVERS_EDITMODE_ADD: { + /* add a new driver with the information obtained (only if valid) */ + ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON); + break; + } + case DRIVERS_EDITMODE_REMOVE: { + /* remove driver matching the information obtained (only if valid) */ + ANIM_remove_driver(reports, id, path, array_index, dflags); + break; } } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_drivers_editop(space_outliner, &te->subtree, reports, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -1956,8 +1949,7 @@ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_ADD); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, nullptr); /* XXX */ @@ -1996,8 +1988,7 @@ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_drivers_editop( - space_outliner, &space_outliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE); + do_outliner_drivers_editop(space_outliner, op->reports, DRIVERS_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, ND_KEYS, nullptr); /* XXX */ @@ -2064,68 +2055,64 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add) return ks; } -/* Recursively iterate over tree, finding and working on selected items */ +/* Iterate over tree, finding and working on selected items */ static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner, KeyingSet *ks, - ListBase *tree, - short mode) + const short mode) { - LISTBASE_FOREACH (TreeElement *, te, tree) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); /* if item is selected, perform operation */ - if (tselem->flag & TSE_SELECTED) { - ID *id = nullptr; - char *path = nullptr; - int array_index = 0; - short flag = 0; - short groupmode = KSP_GROUP_KSNAME; - - /* check if RNA-property described by this selected element is an animatable prop */ - const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); - PointerRNA ptr = te_rna->getPointerRNA(); - if (te_rna && te_rna->getPropertyRNA() && - RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { - /* get id + path + index info from the selected element */ - tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); - } + if (!(tselem->flag & TSE_SELECTED)) { + return; + } - /* only if ID and path were set, should we perform any actions */ - if (id && path) { - /* action depends on mode */ - switch (mode) { - case KEYINGSET_EDITMODE_ADD: { - /* add a new path with the information obtained (only if valid) */ - /* TODO: what do we do with group name? - * for now, we don't supply one, and just let this use the KeyingSet name */ - BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); - ks->active_path = BLI_listbase_count(&ks->paths); - break; - } - case KEYINGSET_EDITMODE_REMOVE: { - /* find the relevant path, then remove it from the KeyingSet */ - KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); + ID *id = nullptr; + char *path = nullptr; + int array_index = 0; + short flag = 0; + short groupmode = KSP_GROUP_KSNAME; + + /* check if RNA-property described by this selected element is an animatable prop */ + const TreeElementRNACommon *te_rna = tree_element_cast<TreeElementRNACommon>(te); + PointerRNA ptr = te_rna->getPointerRNA(); + if (te_rna && te_rna->getPropertyRNA() && + RNA_property_animateable(&ptr, te_rna->getPropertyRNA())) { + /* get id + path + index info from the selected element */ + tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode); + } - if (ksp) { - /* free path's data */ - BKE_keyingset_free_path(ks, ksp); + /* only if ID and path were set, should we perform any actions */ + if (id && path) { + /* action depends on mode */ + switch (mode) { + case KEYINGSET_EDITMODE_ADD: { + /* add a new path with the information obtained (only if valid) */ + /* TODO: what do we do with group name? + * for now, we don't supply one, and just let this use the KeyingSet name */ + BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode); + ks->active_path = BLI_listbase_count(&ks->paths); + break; + } + case KEYINGSET_EDITMODE_REMOVE: { + /* find the relevant path, then remove it from the KeyingSet */ + KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode); - ks->active_path = 0; - } - break; + if (ksp) { + /* free path's data */ + BKE_keyingset_free_path(ks, ksp); + + ks->active_path = 0; } + break; } - - /* free path, since it had to be generated */ - MEM_freeN(path); } - } - /* go over sub-tree */ - if (TSELEM_OPEN(tselem, space_outliner)) { - do_outliner_keyingset_editop(space_outliner, ks, &te->subtree, mode); + /* free path, since it had to be generated */ + MEM_freeN(path); } - } + }); } /** \} */ @@ -2150,7 +2137,7 @@ static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op) } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop(space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_ADD); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_ADD); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); @@ -2191,8 +2178,7 @@ static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(o } /* recursively go into tree, adding selected items */ - do_outliner_keyingset_editop( - space_outliner, ks, &space_outliner->tree, KEYINGSET_EDITMODE_REMOVE); + do_outliner_keyingset_editop(space_outliner, ks, KEYINGSET_EDITMODE_REMOVE); /* send notifiers */ WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, nullptr); diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index f3bcb7b0f1e..a0dcb49aa43 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -160,8 +160,6 @@ enum { /* Child elements of the same type in the icon-row are drawn merged as one icon. * This flag is set for an element that is part of these merged child icons. */ TE_ICONROW_MERGED = (1 << 7), - /* This element has some warning to be displayed. */ - TE_HAS_WARNING = (1 << 8), }; /* button events */ @@ -410,8 +408,12 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel); * Set or unset \a flag for all outliner elements in \a lb and sub-trees. * \return if any flag was modified. */ -bool outliner_flag_set(ListBase *lb, short flag, short set); -bool outliner_flag_flip(ListBase *lb, short flag); +extern "C++" { +bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set); +bool outliner_flag_set(const ListBase &lb, short flag, short set); +bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag); +bool outliner_flag_flip(const ListBase &lb, short flag); +} void item_rename_fn(struct bContext *C, struct ReportList *reports, @@ -453,7 +455,8 @@ void id_remap_fn(struct bContext *C, /** * To retrieve coordinates with redrawing the entire tree. */ -void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner); +void outliner_set_coordinates(const struct ARegion *region, + const struct SpaceOutliner *space_outliner); /** * Open or close a tree element, optionally toggling all children recursively. @@ -510,6 +513,11 @@ void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot); void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); +/* outliner_query.cc ---------------------------------------------- */ + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner); +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner); + /* outliner_tools.c ---------------------------------------------- */ void merged_element_search_menu_invoke(struct bContext *C, diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc new file mode 100644 index 00000000000..d6483c44fce --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_query.cc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include <functional> + +#include "BLI_listbase.h" + +#include "DNA_space_types.h" + +#include "outliner_intern.hh" +#include "tree/tree_display.hh" + +using namespace blender::ed::outliner; + +bool outliner_shows_mode_column(const SpaceOutliner &space_outliner) +{ + const AbstractTreeDisplay &tree_display = *space_outliner.runtime->tree_display; + + return tree_display.supportsModeColumn() && (space_outliner.flag & SO_MODE_COLUMN); +} + +/** + * Iterate over the entire tree (including collapsed sub-elements), probing if any of the elements + * has a warning to be displayed. + */ +bool outliner_has_element_warnings(const SpaceOutliner &space_outliner) +{ + std::function<bool(const ListBase &)> recursive_fn; + + recursive_fn = [&](const ListBase &lb) { + LISTBASE_FOREACH (const TreeElement *, te, &lb) { + if (te->abstract_element && !te->abstract_element->getWarning().is_empty()) { + return true; + } + + if (recursive_fn(te->subtree)) { + return true; + } + } + + return false; + }; + + return recursive_fn(space_outliner.tree); +} diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index fd0ee422df0..bd6d3d89706 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -66,7 +66,9 @@ #include "RNA_prototypes.h" #include "outliner_intern.hh" +#include "tree/tree_display.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" using namespace blender::ed::outliner; @@ -1456,7 +1458,7 @@ void outliner_item_select(bContext *C, /* Clear previous active when activating and clear selection when not extending selection */ const short clear_flag = (activate ? TSE_ACTIVE : 0) | (extend ? 0 : TSE_SELECTED); if (clear_flag) { - outliner_flag_set(&space_outliner->tree, clear_flag, false); + outliner_flag_set(*space_outliner, clear_flag, false); } if (select_flag & OL_ITEM_SELECT) { @@ -1530,7 +1532,7 @@ static void do_outliner_range_select(bContext *C, const bool active_selected = (tselem->flag & TSE_SELECTED); if (!extend) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + outliner_flag_set(*space_outliner, TSE_SELECTED, false); } /* Select active if under cursor */ @@ -1557,12 +1559,11 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *space_ou bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2]) { - /* Mode toggles only show in View Layer and Scenes modes. */ - if (!ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) { + if (!outliner_shows_mode_column(*space_outliner)) { return false; } - return space_outliner->flag & SO_MODE_COLUMN && view_mval[0] < UI_UNIT_X; + return view_mval[0] < UI_UNIT_X; } static bool outliner_is_co_within_active_mode_column(bContext *C, @@ -1604,7 +1605,7 @@ static int outliner_item_do_activate_from_cursor(bContext *C, if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) { if (deselect_all) { - changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false); + changed |= outliner_flag_set(*space_outliner, TSE_SELECTED, false); } } /* Don't allow toggle on scene collection */ @@ -1715,26 +1716,17 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) /** \name Box Select Operator * \{ */ -static void outliner_item_box_select(bContext *C, - SpaceOutliner *space_outliner, - Scene *scene, - rctf *rectf, - TreeElement *te, - bool select) +static void outliner_box_select(bContext *C, + SpaceOutliner *space_outliner, + const rctf *rectf, + const bool select) { - TreeStoreElem *tselem = TREESTORE(te); - - if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { - outliner_item_select( - C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); - } - - /* Look at its children. */ - if (TSELEM_OPEN(tselem, space_outliner)) { - LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) { - outliner_item_box_select(C, space_outliner, scene, rectf, te_sub, select); + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { + if (te->ys <= rectf->ymax && te->ys + UI_UNIT_Y >= rectf->ymin) { + outliner_item_select( + C, space_outliner, te, (select ? OL_ITEM_SELECT : OL_ITEM_DESELECT) | OL_ITEM_EXTEND); } - } + }); } static int outliner_box_select_exec(bContext *C, wmOperator *op) @@ -1747,15 +1739,13 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { - outliner_flag_set(&space_outliner->tree, TSE_SELECTED, 0); + outliner_flag_set(*space_outliner, TSE_SELECTED, 0); } WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) { - outliner_item_box_select(C, space_outliner, scene, &rectf, te, select); - } + outliner_box_select(C, space_outliner, &rectf, select); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 5da64177e51..bfb4c7251a8 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -84,6 +84,7 @@ #include "outliner_intern.hh" #include "tree/tree_element_rna.hh" #include "tree/tree_element_seq.hh" +#include "tree/tree_iterator.hh" static CLG_LogRef LOG = {"ed.outliner.tools"}; @@ -231,43 +232,51 @@ static void unlink_material_fn(bContext *UNUSED(C), Material **matar = nullptr; int a, totcol = 0; - if (GS(tsep->id->name) == ID_OB) { - Object *ob = (Object *)tsep->id; - totcol = ob->totcol; - matar = ob->mat; - } - else if (GS(tsep->id->name) == ID_ME) { - Mesh *me = (Mesh *)tsep->id; - totcol = me->totcol; - matar = me->mat; - } - else if (GS(tsep->id->name) == ID_CU_LEGACY) { - Curve *cu = (Curve *)tsep->id; - totcol = cu->totcol; - matar = cu->mat; - } - else if (GS(tsep->id->name) == ID_MB) { - MetaBall *mb = (MetaBall *)tsep->id; - totcol = mb->totcol; - matar = mb->mat; - } - else if (GS(tsep->id->name) == ID_CV) { - Curves *curves = (Curves *)tsep->id; - totcol = curves->totcol; - matar = curves->mat; - } - else if (GS(tsep->id->name) == ID_PT) { - PointCloud *pointcloud = (PointCloud *)tsep->id; - totcol = pointcloud->totcol; - matar = pointcloud->mat; - } - else if (GS(tsep->id->name) == ID_VO) { - Volume *volume = (Volume *)tsep->id; - totcol = volume->totcol; - matar = volume->mat; - } - else { - BLI_assert(0); + switch (GS(tsep->id->name)) { + case ID_OB: { + Object *ob = (Object *)tsep->id; + totcol = ob->totcol; + matar = ob->mat; + break; + } + case ID_ME: { + Mesh *me = (Mesh *)tsep->id; + totcol = me->totcol; + matar = me->mat; + break; + } + case ID_CU_LEGACY: { + Curve *cu = (Curve *)tsep->id; + totcol = cu->totcol; + matar = cu->mat; + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)tsep->id; + totcol = mb->totcol; + matar = mb->mat; + break; + } + case ID_CV: { + Curves *curves = (Curves *)tsep->id; + totcol = curves->totcol; + matar = curves->mat; + break; + } + case ID_PT: { + PointCloud *pointcloud = (PointCloud *)tsep->id; + totcol = pointcloud->totcol; + matar = pointcloud->mat; + break; + } + case ID_VO: { + Volume *volume = (Volume *)tsep->id; + totcol = volume->totcol; + matar = volume->mat; + break; + } + default: + BLI_assert_unreachable(); } if (LIKELY(matar != nullptr)) { @@ -404,11 +413,10 @@ static void outliner_do_libdata_operation(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, - ListBase *lb, outliner_operation_fn operation_fn, void *user_data) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) || @@ -417,11 +425,7 @@ static void outliner_do_libdata_operation(bContext *C, operation_fn(C, reports, scene, te, tsep, tselem, user_data); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_libdata_operation( - C, reports, scene, space_outliner, &te->subtree, operation_fn, user_data); - } - } + }); } /** \} */ @@ -492,7 +496,7 @@ static int outliner_scene_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Delete Scene(s)"); } else { - BLI_assert(0); + BLI_assert_unreachable(); return OPERATOR_CANCELLED; } @@ -755,6 +759,10 @@ static void id_local_fn(bContext *C, struct OutlinerLibOverrideData { bool do_hierarchy; + + /** When creating new overrides, make them all user-editable. */ + bool do_fully_editable; + /** * For resync operation, force keeping newly created override IDs (or original linked IDs) * instead of re-applying relevant existing ID pointer property override operations. Helps @@ -949,7 +957,8 @@ static void id_override_library_create_fn(bContext *C, id_root_reference, id_hierarchy_root_reference, id_instance_hint, - &id_root_override); + &id_root_override, + data->do_fully_editable); BLI_assert(id_root_override != nullptr); BLI_assert(!ID_IS_LINKED(id_root_override)); @@ -1589,21 +1598,17 @@ static void outliner_do_data_operation( SpaceOutliner *space_outliner, int type, int event, - ListBase *lb, void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *), void *arg) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { operation_fn(event, te, tselem, arg); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_data_operation(space_outliner, type, event, &te->subtree, operation_fn, arg); - } - } + }); } static Base *outliner_batch_delete_hierarchy( @@ -1726,53 +1731,59 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) event = RNA_enum_get(op->ptr, "type"); - if (event == OL_OP_SELECT) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } + switch (event) { + case OL_OP_SELECT: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } - str = "Select Objects"; - selection_changed = true; - } - else if (event == OL_OP_SELECT_HIERARCHY) { - Scene *sce = scene; /* To be able to delete, scenes are set... */ - outliner_do_object_operation_ex(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - object_select_hierarchy_fn, - nullptr, - false); - if (scene != sce) { - WM_window_set_active_scene(bmain, C, win, sce); - } - str = "Select Object Hierarchy"; - selection_changed = true; - } - else if (event == OL_OP_DESELECT) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); - str = "Deselect Objects"; - selection_changed = true; - } - else if (event == OL_OP_REMAP) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth - * trick does not work here). */ - } - else if (event == OL_OP_RENAME) { - outliner_do_object_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); - str = "Rename Object"; - } - else { - BLI_assert(0); - return OPERATOR_CANCELLED; + str = "Select Objects"; + selection_changed = true; + break; + } + case OL_OP_SELECT_HIERARCHY: { + Scene *sce = scene; /* To be able to delete, scenes are set... */ + outliner_do_object_operation_ex(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + object_select_hierarchy_fn, + nullptr, + false); + /* FIXME: This is most certainly broken, maybe check should rather be + * `if (CTX_data_scene(C) != scene)` ? */ + if (scene != sce) { + WM_window_set_active_scene(bmain, C, win, sce); + } + str = "Select Object Hierarchy"; + selection_changed = true; + break; + } + case OL_OP_DESELECT: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); + str = "Deselect Objects"; + selection_changed = true; + break; + case OL_OP_REMAP: + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ + break; + case OL_OP_RENAME: + outliner_do_object_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); + str = "Rename Object"; + break; + default: + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; } if (selection_changed) { @@ -1964,6 +1975,7 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, @@ -1996,7 +2008,7 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Remap Users", "Make all users of selected data-blocks to use instead current (clicked) one"}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", 0, @@ -2009,6 +2021,12 @@ static const EnumPropertyItem prop_id_op_types[] = { "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies - only " "applies to active Outliner item"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, + "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", + 0, + "Make Library Override Hierarchy Fully Editable", + "Make a local override of this linked data-block, and its hierarchy of dependencies, making " + "them all fully user-editable - only applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, "OVERRIDE_LIBRARY_MAKE_EDITABLE", 0, @@ -2049,10 +2067,10 @@ static const EnumPropertyItem prop_id_op_types[] = { "Clear Library Override Hierarchy", "Delete this local override (including its hierarchy of override dependencies) and relink " "its usages to the linked data-blocks"}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, - {0, "", 0, nullptr, nullptr}, + RNA_ENUM_ITEM_SEPR, {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, @@ -2088,6 +2106,7 @@ static bool outliner_id_operation_item_poll(bContext *C, } return false; case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { return true; } @@ -2164,13 +2183,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_UNLINK: { /* unlink datablock from its parent */ if (objectlevel) { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_object_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_object_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Object"); @@ -2179,61 +2193,36 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case ID_MA: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_material_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_material_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink material"); break; case ID_TE: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_texture_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_texture_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr); ED_undo_push(C, "Unlink texture"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Unlink world"); break; case ID_GR: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - unlink_collection_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, unlink_collection_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr); ED_undo_push(C, "Unlink Collection"); @@ -2246,20 +2235,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_LOCAL: { /* make local */ - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_local_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr); ED_undo_push(C, "Localized Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_create_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); ED_undo_push(C, "Overridden Data"); break; } @@ -2270,19 +2253,30 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); + id_override_library_create_hierarchy_post_process(C, &override_data); + + ED_undo_push(C, "Overridden Data Hierarchy"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { + OutlinerLibOverrideData override_data{}; + override_data.do_hierarchy = true; + override_data.do_fully_editable = true; outliner_do_libdata_operation(C, op->reports, scene, space_outliner, - &space_outliner->tree, - id_override_library_create_fn, + id_override_library_create_hierarchy_pre_process_fn, &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); id_override_library_create_hierarchy_post_process(C, &override_data); - ED_undo_push(C, "Overridden Data Hierarchy"); + ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { @@ -2290,7 +2284,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) op->reports, scene, space_outliner, - &space_outliner->tree, id_override_library_toggle_flag_fn, POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED)); @@ -2299,39 +2292,24 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { OutlinerLibOverrideData override_data{}; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_reset_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data); ED_undo_push(C, "Reset Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } @@ -2339,35 +2317,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; override_data.do_resync_hierarchy_enforce = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_resync_fn, - &override_data); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data); ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_hierarchy_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: { - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_override_library_clear_single_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr); ED_undo_push(C, "Clear Overridden Data Hierarchy"); break; } @@ -2375,26 +2338,16 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) /* make single user */ switch (idlevel) { case ID_AC: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_action_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_action_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Single-User Action"); break; case ID_WO: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - singleuser_world_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, singleuser_world_fn, nullptr); WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr); ED_undo_push(C, "Single-User World"); @@ -2409,15 +2362,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_DELETE: { if (idlevel > 0) { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete"); } break; } case OUTLINER_IDOP_REMAP: { if (idlevel > 0) { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ } @@ -2440,13 +2392,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_ADD: { /* set fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_set_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_set_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Add Fake User"); @@ -2454,13 +2401,8 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_FAKE_CLEAR: { /* clear fake user */ - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_fake_user_clear_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_fake_user_clear_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Clear Fake User"); @@ -2469,20 +2411,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_RENAME: { /* rename */ outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); + C, op->reports, scene, space_outliner, item_rename_fn, nullptr); WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); ED_undo_push(C, "Rename"); break; } case OUTLINER_IDOP_SELECT_LINKED: - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - &space_outliner->tree, - id_select_linked_fn, - nullptr); + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, id_select_linked_fn, nullptr); ED_outliner_select_sync_from_all_tag(C); ED_undo_push(C, "Select"); break; @@ -2527,14 +2464,12 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) enum eOutlinerLibOpTypes { OL_LIB_INVALID = 0, - OL_LIB_RENAME, OL_LIB_DELETE, OL_LIB_RELOCATE, OL_LIB_RELOAD, }; static const EnumPropertyItem outliner_lib_op_type_items[] = { - {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, {OL_LIB_DELETE, "DELETE", ICON_X, @@ -2566,30 +2501,20 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type"); switch (event) { - case OL_LIB_RENAME: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, nullptr); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr); - ED_undo_push(C, "Rename Library"); - break; - } case OL_LIB_DELETE: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_fn, nullptr); ED_undo_push(C, "Delete Library"); break; } case OL_LIB_RELOCATE: { outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_relocate_fn, nullptr); + C, op->reports, scene, space_outliner, lib_relocate_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; } case OL_LIB_RELOAD: { - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, &space_outliner->tree, lib_reload_fn, nullptr); + outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn, nullptr); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth * trick does not work here). */ break; @@ -2632,11 +2557,10 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) static void outliner_do_id_set_operation( SpaceOutliner *space_outliner, int type, - ListBase *lb, ID *newid, void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *)) { - LISTBASE_FOREACH (TreeElement *, te, lb) { + tree_iterator::all_open(*space_outliner, [&](TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { if (tselem->type == type) { @@ -2644,10 +2568,7 @@ static void outliner_do_id_set_operation( operation_fn(te, tselem, tsep, newid); } } - if (TSELEM_OPEN(tselem, space_outliner)) { - outliner_do_id_set_operation(space_outliner, type, &te->subtree, newid, operation_fn); - } - } + }); } static void actionset_id_fn(TreeElement *UNUSED(te), @@ -2702,12 +2623,10 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) /* perform action if valid channel */ if (datalevel == TSE_ANIM_DATA) { - outliner_do_id_set_operation( - space_outliner, datalevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, datalevel, (ID *)act, actionset_id_fn); } else if (idlevel == ID_AC) { - outliner_do_id_set_operation( - space_outliner, idlevel, &space_outliner->tree, (ID *)act, actionset_id_fn); + outliner_do_id_set_operation(space_outliner, idlevel, (ID *)act, actionset_id_fn); } else { return OPERATOR_CANCELLED; @@ -2794,8 +2713,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) switch (event) { case OUTLINER_ANIMOP_CLEAR_ADT: /* Remove Animation Data - this may remove the active action, in some cases... */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, clear_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, clear_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Clear Animation Data"); @@ -2812,32 +2730,23 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) case OUTLINER_ANIMOP_CLEAR_ACT: /* clear active action - using standard rules */ - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, unlinkact_animdata_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, unlinkact_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr); ED_undo_push(C, "Unlink action"); break; case OUTLINER_ANIMOP_REFRESH_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - refreshdrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, refreshdrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); // ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */ break; case OUTLINER_ANIMOP_CLEAR_DRV: - outliner_do_data_operation(space_outliner, - datalevel, - event, - &space_outliner->tree, - cleardrivers_animdata_fn, - nullptr); + outliner_do_data_operation( + space_outliner, datalevel, event, cleardrivers_animdata_fn, nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); ED_undo_push(C, "Clear Drivers"); @@ -2887,8 +2796,7 @@ static int outliner_constraint_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropConstraintOps event = (eOutliner_PropConstraintOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C); + outliner_do_data_operation(space_outliner, TSE_CONSTRAINT, event, constraint_fn, C); if (event == OL_CONSTRAINTOP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2934,8 +2842,7 @@ static int outliner_modifier_operation_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); eOutliner_PropModifierOps event = (eOutliner_PropModifierOps)RNA_enum_get(op->ptr, "type"); - outliner_do_data_operation( - space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C); + outliner_do_data_operation(space_outliner, TSE_MODIFIER, event, modifier_fn, C); if (event == OL_MODIFIER_OP_DELETE) { outliner_cleanup_tree(space_outliner); @@ -2978,24 +2885,21 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) eOutliner_PropDataOps event = (eOutliner_PropDataOps)RNA_enum_get(op->ptr, "type"); switch (datalevel) { case TSE_POSE_CHANNEL: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, pchan_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, pchan_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "PoseChannel operation"); break; } case TSE_BONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, bone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, bone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "Bone operation"); break; } case TSE_EBONE: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, ebone_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, ebone_fn, nullptr); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); ED_undo_push(C, "EditBone operation"); @@ -3003,16 +2907,14 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_SEQUENCE: { Scene *scene = CTX_data_scene(C); - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene); + outliner_do_data_operation(space_outliner, datalevel, event, sequence_fn, scene); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); ED_undo_push(C, "Sequencer operation"); break; } case TSE_GP_LAYER: { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, gpencil_layer_fn, nullptr); + outliner_do_data_operation(space_outliner, datalevel, event, gpencil_layer_fn, nullptr); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, nullptr); ED_undo_push(C, "Grease Pencil Layer operation"); @@ -3020,8 +2922,7 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) } case TSE_RNA_STRUCT: if (event == OL_DOP_SELECT_LINKED) { - outliner_do_data_operation( - space_outliner, datalevel, event, &space_outliner->tree, data_select_linked_fn, C); + outliner_do_data_operation(space_outliner, datalevel, event, data_select_linked_fn, C); } break; diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index bbd9b48c260..e6098631a68 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -919,10 +919,6 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } - if (tree_element_warnings_get(te, nullptr, nullptr)) { - te->flag |= TE_HAS_WARNING; - } - return te; } @@ -1686,6 +1682,9 @@ void outliner_build_tree(Main *mainvar, space_outliner->storeflag &= ~SO_TREESTORE_REBUILD; if (region->do_draw & RGN_DRAW_NO_REBUILD) { + BLI_assert_msg(space_outliner->runtime->tree_display != nullptr, + "Skipping rebuild before tree was built properly, a full redraw should be " + "triggered instead"); return; } diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 4b947154864..0db612ce6db 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -27,6 +27,9 @@ #include "UI_view2d.h" #include "outliner_intern.hh" +#include "tree/tree_iterator.hh" + +using namespace blender::ed::outliner; /* -------------------------------------------------------------------- */ /** \name Tree View Context diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 97dc659155f..5bcd1edebc0 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -438,7 +438,7 @@ static void outliner_deactivate(struct ScrArea *area) { /* Remove hover highlights */ SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); - outliner_flag_set(&space_outliner->tree, TSE_HIGHLIGHTED_ANY, false); + outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false); ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW)); } diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 141c68594e8..6ab497b3fbb 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -45,9 +45,9 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode( return nullptr; } -bool AbstractTreeDisplay::hasWarnings() const +bool AbstractTreeDisplay::supportsModeColumn() const { - return has_warnings; + return false; } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 327f29aa15e..190e35c81d6 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -75,12 +75,16 @@ class AbstractTreeDisplay { */ virtual ListBase buildTree(const TreeSourceData &source_data) = 0; - /** Accessor to whether given tree has some warnings to display. */ - bool hasWarnings() const; + /** + * Define if the display mode should be allowed to show a mode column on the left. This column + * adds an icon to indicate which objects are in the current mode (edit mode, pose mode, etc.) + * and allows adding other objects to the mode by clicking the icon. + * + * Returns false by default. + */ + virtual bool supportsModeColumn() const; protected: - bool has_warnings = false; - /** All derived classes will need a handle to this, so storing it in the base for convenience. */ SpaceOutliner &space_outliner_; }; @@ -100,6 +104,8 @@ class TreeDisplayViewLayer final : public AbstractTreeDisplay { ListBase buildTree(const TreeSourceData &source_data) override; + bool supportsModeColumn() const override; + private: void add_view_layer(Scene &, ListBase &, TreeElement *); void add_layer_collections_recursive(ListBase &, ListBase &, TreeElement &); @@ -212,6 +218,8 @@ class TreeDisplayScenes final : public AbstractTreeDisplay { TreeDisplayScenes(SpaceOutliner &space_outliner); ListBase buildTree(const TreeSourceData &source_data) override; + + bool supportsModeColumn() const override; }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index 79eec632c90..405f1dd73f4 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -141,9 +141,6 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, ListBase tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0); tenlib->name = IFACE_("Current File"); } - if (tenlib->flag & TE_HAS_WARNING) { - has_warnings = true; - } } /* Create data-block list parent element on demand. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc index 9e00a425a5a..6b1de7f8b95 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc @@ -26,6 +26,11 @@ TreeDisplayScenes::TreeDisplayScenes(SpaceOutliner &space_outliner) { } +bool TreeDisplayScenes::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayScenes::buildTree(const TreeSourceData &source_data) { /* On first view we open scenes. */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index 19811e45b90..80b3365766a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -55,6 +55,11 @@ TreeDisplayViewLayer::TreeDisplayViewLayer(SpaceOutliner &space_outliner) { } +bool TreeDisplayViewLayer::supportsModeColumn() const +{ + return true; +} + ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 1e3fd2df7c2..94d55b70e3c 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -100,6 +100,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i return nullptr; } +StringRefNull AbstractTreeElement::getWarning() const +{ + return ""; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { @@ -118,39 +123,4 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner tree_element.expand(space_outliner); } -bool tree_element_warnings_get(TreeElement *te, int *r_icon, const char **r_message) -{ - TreeStoreElem *tselem = te->store_elem; - - if (tselem->type != TSE_SOME_ID) { - return false; - } - if (te->idcode != ID_LI) { - return false; - } - - Library *library = (Library *)tselem->id; - if (library->tag & LIBRARY_TAG_RESYNC_REQUIRED) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_( - "Contains linked library overrides that need to be resynced, updating the library is " - "recommended"); - } - return true; - } - if (library->id.tag & LIB_TAG_MISSING) { - if (r_icon) { - *r_icon = ICON_ERROR; - } - if (r_message) { - *r_message = TIP_("Missing library"); - } - return true; - } - return false; -} - } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index c6593a517dd..0dcd75d340d 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -8,6 +8,8 @@ #include <memory> +#include "BLI_string_ref.hh" + struct ListBase; struct SpaceOutliner; struct TreeElement; @@ -56,6 +58,12 @@ class AbstractTreeElement { } /** + * By letting this return a warning message, the tree element will display a warning icon with + * the message in the tooltip. + */ + virtual StringRefNull getWarning() const; + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * @@ -96,13 +104,4 @@ struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner, void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner); -/** - * 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. - * \return true if there is a warning, false otherwise. - */ -bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message); - } // namespace blender::ed::outliner 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 ef5e056f229..86f5fd4eff5 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -27,6 +27,11 @@ namespace blender::ed::outliner { std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_te, ID &id) { + if (ID_TYPE_IS_DEPRECATED(GS(id.name))) { + BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); + return nullptr; + } + switch (ID_Type type = GS(id.name); type) { case ID_LI: return std::make_unique<TreeElementIDLibrary>(legacy_te, (Library &)id); @@ -70,10 +75,9 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t case ID_PC: case ID_CF: return std::make_unique<TreeElementID>(legacy_te, id); - /* Deprecated */ case ID_IP: - BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); - return nullptr; + BLI_assert_unreachable(); + break; } return nullptr; diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc index 0dcaec0385a..4f1b951ccaf 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.cc @@ -4,6 +4,8 @@ * \ingroup spoutliner */ +#include "BLT_translation.h" + #include "DNA_ID.h" #include "DNA_listBase.h" @@ -24,4 +26,21 @@ bool TreeElementIDLibrary::isExpandValid() const return true; } +StringRefNull TreeElementIDLibrary::getWarning() const +{ + Library &library = reinterpret_cast<Library &>(id_); + + if (library.tag & LIBRARY_TAG_RESYNC_REQUIRED) { + return TIP_( + "Contains linked library overrides that need to be resynced, updating the library is " + "recommended"); + } + + if (library.id.tag & LIB_TAG_MISSING) { + return TIP_("Missing library"); + } + + return {}; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh index ed599cf04da..2d89b55813f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id_library.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_id_library.hh @@ -17,6 +17,8 @@ class TreeElementIDLibrary final : public TreeElementID { TreeElementIDLibrary(TreeElement &legacy_te, Library &library); bool isExpandValid() const override; + + blender::StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 3a039da86c2..53e7b88c923 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -38,6 +38,19 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i } } +StringRefNull TreeElementOverridesBase::getWarning() const +{ + if (id.flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { + return TIP_("This override data-block is not needed anymore, but was detected as user-edited"); + } + + if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) { + return TIP_("This override data-block is unused"); + } + + return {}; +} + void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const { BLI_assert(id.override_library != nullptr); @@ -93,4 +106,15 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t legacy_te.name = override_data.override_property.rna_path; } +StringRefNull TreeElementOverridesProperty::getWarning() const +{ + if (!is_rna_path_valid) { + return TIP_( + "This override property does not exist in current data, it will be removed on " + "next .blend file save"); + } + + return {}; +} + } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index b42e1c37a0f..1db46d9af1d 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -34,6 +34,8 @@ class TreeElementOverridesBase final : public AbstractTreeElement { TreeElementOverridesBase(TreeElement &legacy_te, ID &id); void expand(SpaceOutliner &) const override; + + StringRefNull getWarning() const override; }; class TreeElementOverridesProperty final : public AbstractTreeElement { @@ -46,6 +48,8 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); + + StringRefNull getWarning() const override; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.cc b/source/blender/editors/space_outliner/tree/tree_iterator.cc new file mode 100644 index 00000000000..85ff9e6437e --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.cc @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" + +#include "../outliner_intern.hh" + +#include "tree_iterator.hh" + +namespace blender::ed::outliner::tree_iterator { + +void all(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all(const ListBase &subtree, const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. */ + + all(subtree, visitor); + } +} + +void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor) +{ + all_open(space_outliner, space_outliner.tree, visitor); +} + +void all_open(const SpaceOutliner &space_outliner, + const ListBase &subtree, + const VisitorFn visitor) +{ + LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) { + /* Get needed data out in case element gets freed. */ + const bool is_open = TSELEM_OPEN(element->store_elem, &space_outliner); + const ListBase subtree = element->subtree; + + visitor(element); + /* Don't access element from now on, it may be freed. */ + + if (is_open) { + all_open(space_outliner, subtree, visitor); + } + } +} + +} // namespace blender::ed::outliner::tree_iterator diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh new file mode 100644 index 00000000000..e3b3c90eaad --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include "BLI_function_ref.hh" + +struct ListBase; +struct SpaceOutliner; +struct TreeElement; + +namespace blender::ed::outliner { +namespace tree_iterator { + +using VisitorFn = FunctionRef<void(TreeElement *)>; + +/** + * Preorder (meaning depth-first) traversal of all elements (regardless of collapsed state). + * Freeing the currently visited element in \a visitor is fine. + */ +void all(const SpaceOutliner &space_outliner, VisitorFn visitor); +void all(const ListBase &subtree, VisitorFn visitor); + +/** + * Preorder (meaning depth-first) traversal of all elements not part of a collapsed sub-tree. + * Freeing the currently visited element in \a visitor is fine. + */ +void all_open(const SpaceOutliner &, VisitorFn visitor); +void all_open(const SpaceOutliner &, const ListBase &subtree, VisitorFn visitor); + +} // namespace tree_iterator +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index b5355efec7a..44f919ca361 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -25,10 +25,10 @@ set(INC set(SRC sequencer_add.c sequencer_buttons.c - sequencer_drag_drop.c - sequencer_draw.c sequencer_channels_draw.c sequencer_channels_edit.c + sequencer_drag_drop.c + sequencer_draw.c sequencer_edit.c sequencer_modifier.c sequencer_ops.c diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 469169cf4cc..10c6e828e96 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -31,6 +31,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_sound.h" #include "IMB_imbuf.h" @@ -54,6 +55,7 @@ #include "SEQ_transform.h" #include "SEQ_utils.h" +#include "ED_scene.h" /* For menu, popup, icons, etc. */ #include "ED_screen.h" #include "ED_sequencer.h" @@ -469,6 +471,125 @@ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot) ot->prop = prop; } +static EnumPropertyItem strip_new_scene_items[] = { + {SCE_COPY_NEW, "NEW", 0, "New", "Add new Strip with a new empty Scene with default settings"}, + {SCE_COPY_EMPTY, + "EMPTY", + 0, + "Copy Settings", + "Add a new Strip, with an empty scene, and copy settings from the current scene"}, + {SCE_COPY_LINK_COLLECTION, + "LINK_COPY", + 0, + "Linked Copy", + "Add a Strip and link in the collections from the current scene (shallow copy)"}, + {SCE_COPY_FULL, + "FULL_COPY", + 0, + "Full Copy", + "Add a Strip and make a full copy of the current scene"}, + {0, NULL, 0, NULL, NULL}, +}; + +static int sequencer_add_scene_strip_new_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_ensure(scene); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } + + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + + int type = RNA_enum_get(op->ptr, "type"); + Scene *scene_new = ED_scene_sequencer_add(bmain, C, type, false); + if (scene_new == NULL) { + return OPERATOR_CANCELLED; + } + load_data.scene = scene_new; + + Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_add_scene_strip_new_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + sequencer_disable_one_time_properties(C, op); + sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_SCENE); + return sequencer_add_scene_strip_new_exec(C, op); +} + +static const EnumPropertyItem *strip_new_sequencer_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + EnumPropertyItem *item = NULL; + int totitem = 0; + uint item_index; + + item_index = RNA_enum_from_value(strip_new_scene_items, SCE_COPY_NEW); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + + bool has_scene_or_no_context = false; + if (C == NULL) { + /* For documentation generation. */ + has_scene_or_no_context = true; + } + else { + Scene *scene = CTX_data_scene(C); + Sequence *seq = SEQ_select_active_get(scene); + if ((seq && (seq->type == SEQ_TYPE_SCENE) && (seq->scene != NULL))) { + has_scene_or_no_context = true; + } + } + + if (has_scene_or_no_context) { + int values[] = {SCE_COPY_EMPTY, SCE_COPY_LINK_COLLECTION, SCE_COPY_FULL}; + for (int i = 0; i < ARRAY_SIZE(values); i++) { + item_index = RNA_enum_from_value(strip_new_scene_items, values[i]); + RNA_enum_item_add(&item, &totitem, &strip_new_scene_items[item_index]); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} + +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Add Strip with a new Scene"; + ot->idname = "SEQUENCER_OT_scene_strip_add_new"; + ot->description = "Create a new Strip and add a assign a new Scene as source"; + + /* Api callbacks. */ + ot->invoke = sequencer_add_scene_strip_new_invoke; + ot->exec = sequencer_add_scene_strip_new_exec; + ot->poll = ED_operator_sequencer_active_editable; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME); + + ot->prop = RNA_def_enum(ot->srna, "type", strip_new_scene_items, SCE_COPY_NEW, "Type", ""); + RNA_def_enum_funcs(ot->prop, strip_new_sequencer_enum_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -672,8 +793,8 @@ static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene, return; } - SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie)); - SEQ_transform_set_left_handle_frame(seq_sound, SEQ_transform_get_left_handle_frame(seq_movie)); + SEQ_time_right_handle_frame_set(seq_sound, SEQ_time_right_handle_frame_get(seq_movie)); + SEQ_time_left_handle_frame_set(seq_sound, SEQ_time_left_handle_frame_get(seq_movie)); SEQ_time_update_sequence(scene, seqbase, seq_sound); } @@ -1179,7 +1300,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) /* Adjust length. */ if (load_data.image.len == 1) { - SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame); + SEQ_time_right_handle_frame_set(seq, load_data.image.end_frame); SEQ_time_update_sequence(scene, SEQ_active_seqbase_get(ed), seq); } diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.c b/source/blender/editors/space_sequencer/sequencer_channels_draw.c index a777132e9fc..c11388e8555 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.c @@ -200,7 +200,7 @@ static float text_size_get(const SeqChannelDrawContext *context) return UI_fontstyle_height_max(&style->widget) * 1.5f * context->scale; } -/* Todo: decide what gets priority - label or buttons */ +/* TODO: decide what gets priority - label or buttons. */ static rctf label_rect_init(const SeqChannelDrawContext *context, const int channel_index, const float used_width) @@ -286,7 +286,7 @@ static void draw_channel_labels(const SeqChannelDrawContext *context, } } -/* Todo: different text/buttons alignment */ +/* TODO: different text/buttons alignment. */ static void draw_channel_header(const SeqChannelDrawContext *context, uiBlock *block, const int channel_index) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index a561de1ef64..02e77732e02 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1098,26 +1098,23 @@ static void draw_seq_background(Scene *scene, /* Draw the main strip body. */ if (is_single_image) { - immRectf(pos, - SEQ_transform_get_left_handle_frame(seq), - y1, - SEQ_transform_get_right_handle_frame(seq), - y2); + immRectf( + pos, SEQ_time_left_handle_frame_get(seq), y1, SEQ_time_right_handle_frame_get(seq), y2); } else { immRectf(pos, x1, y1, x2, y2); } /* Draw background for hold still regions. */ - if (!is_single_image && (seq->startstill || seq->endstill)) { + if (!is_single_image && SEQ_time_has_still_frames(seq)) { UI_GetColorPtrShade3ubv(col, col, -35); immUniformColor4ubv(col); - if (seq->startstill) { + if (SEQ_time_has_left_still_frames(seq)) { const float content_start = min_ff(seq->enddisp, seq->start); immRectf(pos, seq->startdisp, y1, content_start, y2); } - if (seq->endstill) { + if (SEQ_time_has_right_still_frames(seq)) { const float content_end = max_ff(seq->startdisp, seq->start + seq->len); immRectf(pos, content_end, y1, seq->enddisp, y2); } @@ -1336,9 +1333,9 @@ static void draw_seq_strip(const bContext *C, SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); /* Draw strip body. */ - x1 = (seq->startstill) ? seq->start : seq->startdisp; + x1 = SEQ_time_has_left_still_frames(seq) ? seq->start : seq->startdisp; y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; - x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + x2 = SEQ_time_has_right_still_frames(seq) ? (seq->start + seq->len) : seq->enddisp; y2 = seq->machine + SEQ_STRIP_OFSTOP; /* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */ diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 0305ad279a0..08f98dfb161 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -57,6 +57,7 @@ #include "ED_keyframing.h" #include "ED_numinput.h" #include "ED_outliner.h" +#include "ED_scene.h" #include "ED_screen.h" #include "ED_sequencer.h" @@ -76,7 +77,6 @@ typedef struct TransSeq { int start, machine; - int startstill, endstill; int startdisp, enddisp; int startofs, endofs; int anim_startofs, anim_endofs; @@ -357,15 +357,14 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) if (seq->flag & SELECT && !SEQ_transform_is_locked(channels, seq) && SEQ_transform_sequence_can_be_translated(seq)) { if ((seq->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) == 0) { - SEQ_transform_translate_sequence( - scene, seq, (snap_frame - seq->startofs + seq->startstill) - seq->start); + SEQ_transform_translate_sequence(scene, seq, (snap_frame - seq->startofs) - seq->start); } else { if (seq->flag & SEQ_LEFTSEL) { - SEQ_transform_set_left_handle_frame(seq, snap_frame); + SEQ_time_left_handle_frame_set(seq, snap_frame); } else { /* SEQ_RIGHTSEL */ - SEQ_transform_set_right_handle_frame(seq, snap_frame); + SEQ_time_right_handle_frame_set(seq, snap_frame); } SEQ_transform_handle_xlimits(seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); @@ -478,8 +477,6 @@ static void transseq_backup(TransSeq *ts, Sequence *seq) { ts->start = seq->start; ts->machine = seq->machine; - ts->startstill = seq->startstill; - ts->endstill = seq->endstill; ts->startdisp = seq->startdisp; ts->enddisp = seq->enddisp; ts->startofs = seq->startofs; @@ -493,8 +490,6 @@ static void transseq_restore(TransSeq *ts, Sequence *seq) { seq->start = ts->start; seq->machine = ts->machine; - seq->startstill = ts->startstill; - seq->endstill = ts->endstill; seq->startdisp = ts->startdisp; seq->enddisp = ts->enddisp; seq->startofs = ts->startofs; @@ -595,11 +590,8 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve return OPERATOR_RUNNING_MODAL; } -static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) +static void sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { - /* Only data types supported for now. */ - bool changed = false; - /* Iterate in reverse so meta-strips are iterated after their children. */ for (int i = data->num_seq - 1; i >= 0; i--) { Sequence *seq = data->seq_array[i]; @@ -613,33 +605,13 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) endframe = seq->start + seq->len; /* Compute the sequence offsets. */ - if (endframe > seq->enddisp) { - seq->endstill = 0; - seq->endofs = endframe - seq->enddisp; - changed = true; - } - else { - seq->endstill = seq->enddisp - endframe; - seq->endofs = 0; - changed = true; - } - - if (seq->start > seq->startdisp) { - seq->startstill = seq->start - seq->startdisp; - seq->startofs = 0; - changed = true; - } - else { - seq->startstill = 0; - seq->startofs = seq->startdisp - seq->start; - changed = true; - } + seq->endofs = endframe - seq->enddisp; + seq->startofs = seq->startdisp - seq->start; } else { /* No transform data (likely effect strip). Only move start and end. */ seq->startdisp = data->ts[i].startdisp + offset; seq->enddisp = data->ts[i].enddisp + offset; - changed = true; } /* Effects are only added if we they are in a meta-strip. @@ -651,13 +623,11 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) SEQ_time_update_sequence(scene, seqbase, seq); } } - if (changed) { - for (int i = data->num_seq - 1; i >= 0; i--) { - Sequence *seq = data->seq_array[i]; - SEQ_relations_invalidate_cache_preprocessed(scene, seq); - } + + for (int i = data->num_seq - 1; i >= 0; i--) { + Sequence *seq = data->seq_array[i]; + SEQ_relations_invalidate_cache_preprocessed(scene, seq); } - return changed; } /* Make sure, that each strip contains at least 1 frame of content. */ @@ -687,7 +657,6 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); int offset = RNA_int_get(op->ptr, "offset"); - bool success = false; /* Recursively count the trimmed elements. */ int num_seq = slip_count_sequences_recursive(ed->seqbasep, true); @@ -709,19 +678,16 @@ static int sequencer_slip_exec(bContext *C, wmOperator *op) } sequencer_slip_apply_limits(data, &offset); - success = sequencer_slip_recursively(scene, data, offset); + sequencer_slip_recursively(scene, data, offset); MEM_freeN(data->seq_array); MEM_freeN(data->trim); MEM_freeN(data->ts); MEM_freeN(data); - if (success) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + return OPERATOR_FINISHED; } static void sequencer_slip_update_header(Scene *scene, ScrArea *area, SlipData *data, int offset) @@ -762,9 +728,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_RUNNING_MODAL; } @@ -795,9 +760,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } break; } @@ -875,9 +839,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even RNA_int_set(op->ptr, "offset", offset); - if (sequencer_slip_recursively(scene, data, offset)) { - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - } + sequencer_slip_recursively(scene, data, offset); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); } return OPERATOR_RUNNING_MODAL; @@ -1719,11 +1682,26 @@ void SEQUENCER_OT_duplicate(wmOperatorType *ot) /** \name Erase Strips Operator * \{ */ -static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) +static void sequencer_delete_strip_data(bContext *C, Sequence *seq) +{ + if (seq->type != SEQ_TYPE_SCENE) { + return; + } + + Main *bmain = CTX_data_main(C); + if (seq->scene) { + if (ED_scene_delete(C, bmain, seq->scene)) { + WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, seq->scene); + } + } +} + +static int sequencer_delete_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + const bool delete_data = RNA_boolean_get(op->ptr, "delete_data"); if (sequencer_view_has_preview_poll(C) && !sequencer_view_preview_only_poll(C)) { return OPERATOR_CANCELLED; @@ -1736,6 +1714,9 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) SEQ_ITERATOR_FOREACH (seq, selected_strips) { SEQ_edit_flag_for_removal(scene, seqbasep, seq); + if (delete_data) { + sequencer_delete_strip_data(C, seq); + } } SEQ_edit_remove_flagged_sequences(scene, seqbasep); @@ -1778,6 +1759,14 @@ void SEQUENCER_OT_delete(wmOperatorType *ot) /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_boolean(ot->srna, + "delete_data", + false, + "Delete Data", + "After removing the Strip, delete the associated data also"); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } /** \} */ @@ -1795,7 +1784,7 @@ static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op)) /* For effects, try to find a replacement input. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if ((seq->type & SEQ_TYPE_EFFECT) == 0 && (seq->flag & SELECT)) { - seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0; + seq->startofs = seq->endofs = 0; } } @@ -1869,8 +1858,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* TODO: remove f-curve and assign to split image strips. * The old animation system would remove the user of `seq->ipo`. */ - start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq); - frame_end = SEQ_transform_get_right_handle_frame(seq); + start_ofs = timeline_frame = SEQ_time_left_handle_frame_get(seq); + frame_end = SEQ_time_right_handle_frame_get(seq); while (timeline_frame < frame_end) { /* New seq. */ @@ -1881,7 +1870,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) seq_new->start = start_ofs; seq_new->type = SEQ_TYPE_IMAGE; seq_new->len = 1; - seq_new->endstill = step - 1; + seq_new->endofs = 1 - step; /* New strip. */ strip_new = seq_new->strip; @@ -2596,7 +2585,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) } /* Paste animation. - * Note: Only fcurves are copied. Drivers and NLA action strips are not copied. + * NOTE: Only fcurves are copied. Drivers and NLA action strips are not copied. * First backup original curves from scene and move curves from clipboard into scene. This way, * when pasted strips are renamed, pasted fcurves are renamed with them. Finally restore original * curves from backup. @@ -3040,6 +3029,81 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Change Strip Scene Operator + * \{ */ + +static bool sequencer_strip_change_scene_poll(bContext *C) +{ + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); + if (ed == NULL) { + return false; + } + Sequence *seq = ed->act_seq; + return ((seq != NULL) && (seq->type == SEQ_TYPE_SCENE)); +} +static int sequencer_change_scene_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Scene *scene_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); + + if (scene_seq == NULL) { + BKE_report(op->reports, RPT_ERROR, "Scene not found"); + return OPERATOR_CANCELLED; + } + + /* Assign new scene. */ + Sequence *seq = SEQ_select_active_get(scene); + if (seq) { + seq->scene = scene_seq; + /* Do a refresh of the sequencer data. */ + SEQ_relations_invalidate_cache_raw(scene, seq); + DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS); + DEG_relations_tag_update(bmain); + } + + WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +static int sequencer_change_scene_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "scene")) { + return WM_enum_search_invoke(C, op, event); + } + + return sequencer_change_scene_exec(C, op); +} + +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers. */ + ot->name = "Change Scene"; + ot->idname = "SEQUENCER_OT_change_scene"; + ot->description = "Change Scene assigned to Strip"; + + /* Api callbacks. */ + ot->exec = sequencer_change_scene_exec; + ot->invoke = sequencer_change_scene_invoke; + ot->poll = sequencer_strip_change_scene_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", ""); + RNA_def_enum_funcs(prop, RNA_scene_without_active_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Export Subtitles Operator * \{ */ @@ -3049,8 +3113,8 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b) Sequence *seq_a = (Sequence *)a; Sequence *seq_b = (Sequence *)b; - int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); - int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); + int seq_a_start = SEQ_time_left_handle_frame_get(seq_a); + int seq_b_start = SEQ_time_left_handle_frame_get(seq_b); /* If strips have the same start frame favor the one with a higher channel. */ if (seq_a_start == seq_b_start) { diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 67df065ef35..3307c3fde2f 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -197,6 +197,7 @@ void SEQUENCER_OT_rendersize(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_input(struct wmOperatorType *ot); void SEQUENCER_OT_change_effect_type(struct wmOperatorType *ot); void SEQUENCER_OT_change_path(struct wmOperatorType *ot); +void SEQUENCER_OT_change_scene(struct wmOperatorType *ot); void SEQUENCER_OT_copy(struct wmOperatorType *ot); void SEQUENCER_OT_paste(struct wmOperatorType *ot); @@ -231,6 +232,7 @@ void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot); /* sequencer_add.c */ void SEQUENCER_OT_scene_strip_add(struct wmOperatorType *ot); +void SEQUENCER_OT_scene_strip_add_new(struct wmOperatorType *ot); void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot); void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 1aa2991f07a..f7a9bcf41e6 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -58,6 +58,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_effect_input); WM_operatortype_append(SEQUENCER_OT_change_effect_type); WM_operatortype_append(SEQUENCER_OT_change_path); + WM_operatortype_append(SEQUENCER_OT_change_scene); WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); @@ -81,6 +82,7 @@ void sequencer_operatortypes(void) /* sequencer_add.c */ WM_operatortype_append(SEQUENCER_OT_scene_strip_add); + WM_operatortype_append(SEQUENCER_OT_scene_strip_add_new); WM_operatortype_append(SEQUENCER_OT_movieclip_strip_add); WM_operatortype_append(SEQUENCER_OT_mask_strip_add); WM_operatortype_append(SEQUENCER_OT_movie_strip_add); diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index 43c5e004040..eab17d876f3 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -23,6 +23,7 @@ #include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "SEQ_time.h" #include "WM_api.h" #include "WM_types.h" @@ -443,7 +444,8 @@ void draw_seq_strip_thumbnail(View2D *v2d, float thumb_y_end = y1 + thumb_height; float cut_off = 0; - float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + float upper_thumb_bound = SEQ_time_has_right_still_frames(seq) ? (seq->start + seq->len) : + seq->enddisp; if (seq->type == SEQ_TYPE_IMAGE) { upper_thumb_bound = seq->enddisp; } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index b3dbc3c72d1..93641375d42 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -20,8 +20,6 @@ #include "UI_view2d.h" -#include "RNA_define.h" - #include "SEQ_iterator.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index b3d6c395e89..a498e5e99cf 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -32,8 +32,6 @@ #include "BLF_api.h" -#include "spreadsheet_intern.hh" - #include "spreadsheet_context.hh" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_dataset_draw.hh" diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 4afa70d9ef6..ac15b77fde0 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -64,7 +64,7 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( void GeometryDataSource::foreach_default_column_ids( FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { - if (component_->attribute_domain_size(domain_) == 0) { + if (component_->attribute_domain_num(domain_) == 0) { return; } @@ -81,6 +81,9 @@ void GeometryDataSource::foreach_default_column_ids( if (attribute_id.is_anonymous()) { return true; } + if (!bke::allow_procedural_attribute_access(attribute_id.name())) { + return true; + } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); fn(column_id, false); @@ -110,8 +113,8 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { - const int domain_size = component_->attribute_domain_size(domain_); - if (domain_size == 0) { + const int domain_num = component_->attribute_domain_num(domain_); + if (domain_num == 0) { return {}; } @@ -129,7 +132,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<InstanceReference> references = instances.references(); return std::make_unique<ColumnValues>( column_id.name, - VArray<InstanceReference>::ForFunc(domain_size, + VArray<InstanceReference>::ForFunc(domain_num, [reference_handles, references](int64_t index) { return references[reference_handles[index]]; })); @@ -137,13 +140,13 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( Span<float4x4> transforms = instances.instance_transforms(); if (STREQ(column_id.name, "Rotation")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].to_euler(); })); } if (STREQ(column_id.name, "Scale")) { return std::make_unique<ColumnValues>( - column_id.name, VArray<float3>::ForFunc(domain_size, [transforms](int64_t index) { + column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) { return transforms[index].scale(); })); } @@ -210,7 +213,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( int GeometryDataSource::tot_rows() const { - return component_->attribute_domain_size(domain_); + return component_->attribute_domain_num(domain_); } /** @@ -256,7 +259,7 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c BMesh *bm = mesh_orig->edit_mesh->bm; BM_mesh_elem_table_ensure(bm, BM_VERT); - int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); + const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); if (orig_indices != nullptr) { /* Use CD_ORIGINDEX layer if it exists. */ VArray<bool> selection = mesh_component->attribute_try_adapt_domain<bool>( @@ -524,17 +527,17 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, std::make_unique<GeometryComponentCacheKey>(component)); const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); for (const auto item : fields_to_show.items()) { StringRef name = item.key; const GField &field = item.value; /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { - GArray<> evaluated_array(field.cpp_type(), domain_size); + GArray<> evaluated_array(field.cpp_type(), domain_num); bke::GeometryComponentFieldContext field_context{component, domain}; - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add_with_destination(field, evaluated_array); field_evaluator.evaluate(); return evaluated_array; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index c4b5228758c..724d0783707 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -144,7 +144,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) /* Using the tree row button instead of a separate right aligned button gives padding * to the right side of the number, which it didn't have with the button. */ char element_count[7]; - BLI_str_format_attribute_domain_size(element_count, *count); + BLI_str_format_decimal_unit(element_count, *count); UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); } } @@ -194,7 +194,7 @@ std::optional<int> GeometryDataSetTreeViewItem::count() const } if (const GeometryComponent *component = geometry.get_component_for_read(component_type_)) { - return component->attribute_domain_size(*domain_); + return component->attribute_domain_num(*domain_); } return 0; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc index 4d6dc3b4166..d76bbbf3be6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc @@ -5,12 +5,6 @@ #include "ED_screen.h" -#include "RNA_access.h" -#include "RNA_define.h" - -#include "WM_api.h" -#include "WM_types.h" - #include "BLI_listbase.h" #include "MEM_guardedalloc.h" @@ -20,8 +14,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "ED_screen.h" - #include "WM_api.h" #include "WM_types.h" diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index 3ae4536b652..91ce5c2f6ec 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -14,8 +14,6 @@ #include "RNA_access.h" -#include "spreadsheet_intern.hh" - #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 36eafedbc80..c93ffccd477 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1603,7 +1603,7 @@ void draw_text_main(SpaceText *st, ARegion *region) return; } - /* dpi controlled line height and font size */ + /* DPI controlled line height and font size. */ st->runtime.lheight_px = (U.widget_unit * st->lheight) / 20; /* don't draw lines below this */ diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 7776119a594..8add6886584 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -6,12 +6,14 @@ */ #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_object.h" diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 96a193cc10d..1e54afe46f0 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -12,7 +12,6 @@ #include "BLI_listbase.h" #include "BLI_rect.h" -#include "DNA_scene_types.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index e52ff062302..a65e9a8d506 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -608,7 +608,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); + WM_operator_properties_id_lookup(ot, true); WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, @@ -631,13 +631,20 @@ static int background_image_remove_exec(bContext *C, wmOperator *op) CameraBGImage *bgpic_rem = BLI_findlink(&cam->bg_images, index); if (bgpic_rem) { - if (bgpic_rem->source == CAM_BGIMG_SOURCE_IMAGE) { - id_us_min((ID *)bgpic_rem->ima); - } - else if (bgpic_rem->source == CAM_BGIMG_SOURCE_MOVIE) { - id_us_min((ID *)bgpic_rem->clip); + if (ID_IS_OVERRIDE_LIBRARY(cam) && + (bgpic_rem->flag & CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL) == 0) { + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot remove background image %d from camera '%s', as it is from the linked " + "reference data", + index, + cam->id.name + 2); + return OPERATOR_CANCELLED; } + id_us_min((ID *)bgpic_rem->ima); + id_us_min((ID *)bgpic_rem->clip); + BKE_camera_background_image_remove(cam, bgpic_rem); WM_event_add_notifier(C, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, cam); @@ -657,7 +664,7 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = background_image_remove_exec; - ot->poll = ED_operator_camera; + ot->poll = ED_operator_camera_poll; /* flags */ ot->flag = 0; @@ -678,10 +685,8 @@ static int drop_world_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - - RNA_string_get(op->ptr, "name", name); - World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name); + World *world = (World *)WM_operator_properties_id_lookup_from_name_or_session_uuid( + bmain, op->ptr, ID_WO); if (world == NULL) { return OPERATOR_CANCELLED; } @@ -718,7 +723,7 @@ void VIEW3D_OT_drop_world(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; /* properties */ - RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign"); + WM_operator_properties_id_lookup(ot, true); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index f0557205e8f..aa287403e23 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -19,8 +19,6 @@ #include "MEM_guardedalloc.h" -#include "WM_toolsystem.h" - #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 85d239507ce..1230515a180 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -30,7 +30,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], float r_co[2], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -52,7 +52,7 @@ void ED_view3d_project_float_v2_m4(const ARegion *region, void ED_view3d_project_float_v3_m4(const ARegion *region, const float co[3], float r_co[3], - float mat[4][4]) + const float mat[4][4]) { float vec4[4]; @@ -312,7 +312,7 @@ float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float return -dot_v3v3(rv3d->viewinv[2], co); } -static void view3d_win_to_ray_segment(struct Depsgraph *depsgraph, +static void view3d_win_to_ray_segment(const struct Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], @@ -642,9 +642,9 @@ void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r normalize_v3(r_out); } -bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, +bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph, const ARegion *region, - View3D *v3d, + const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d99063a9b26..efe89621e7b 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -126,6 +126,7 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc, Depsgraph *depsgra void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) { vc->obact = obact; + /* See public doc-string for rationale on checking the existing values first. */ if (vc->obedit) { BLI_assert(BKE_object_is_in_editmode(obact)); vc->obedit = obact; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 51f50633468..306394ce53d 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -106,7 +106,7 @@ void ED_view3d_dist_range_get(const View3D *v3d, float r_dist_range[2]) r_dist_range[1] = v3d->clip_end * 10.0f; } -bool ED_view3d_clip_range_get(Depsgraph *depsgraph, +bool ED_view3d_clip_range_get(const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d, float *r_clipsta, diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 7ae041f2b2f..a7e2f616c9e 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1174,7 +1174,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) stopConstraint(t); initSelectConstraint(t); - /* In this case we might just want to remove the contraint, + /* In this case we might just want to remove the constraint, * so set #TREDRAW_SOFT to only select the constraint on the next mouse move event. * This way we can kind of "cancel" due to confirmation without constraint. */ t->redraw = TREDRAW_SOFT; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index dbe67bd0d66..018468dcd03 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,6 +479,45 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { bool clipx = true, clipy = true; @@ -517,31 +556,29 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && - t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { - vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); - } - else if (max[0] > (base_offset[0] + t->aspect[0]) && - t->center_global[0] < (base_offset[0] + t->aspect[0])) { - vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / - (t->center_global[0] - max[0]); - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && - t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { - vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); - } - else if (max[1] > (base_offset[1] + t->aspect[1]) && - t->center_global[1] < (base_offset[1] + t->aspect[1])) { - vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / - (t->center_global[1] - max[1]); - } - else { - clipy = 0; - } + /* Assume no change is required. */ + float scalex = 1.0f; + float scaley = 1.0f; + + /* Update U against the left border. */ + constrain_scale_to_boundary( + t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], + max[0] - t->center_global[0], + &scalex); + + /* Do the same for the V co-ordinate, which is called `y`. */ + constrain_scale_to_boundary( + t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); + constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], + max[1] - t->center_global[1], + &scaley); + + clipx = (scalex != 1.0f); + clipy = (scaley != 1.0f); + vec[0] *= scalex; + vec[1] *= scaley; } else { if (min[0] < base_offset[0]) { diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 685c35489de..2fa8fcf65ba 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -26,12 +26,9 @@ #include "RNA_prototypes.h" #include "transform.h" -#include "transform_snap.h" - #include "transform_convert.h" -#include "transform_snap.h" - #include "transform_mode.h" +#include "transform_snap.h" /** Used for NLA transform (stored in #TransData.extra pointer). */ typedef struct TransDataNla { diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index d5d79bedbf4..8281052c314 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -46,7 +46,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const } /* use top-left corner as the transform origin for nodes */ - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ #ifdef USE_NODE_CENTER td2d->loc[0] = (locx * dpi_fac) + (BLI_rctf_size_x(&node->totr) * +0.5f); td2d->loc[1] = (locy * dpi_fac) + (BLI_rctf_size_y(&node->totr) * -0.5f); @@ -194,7 +194,7 @@ void flushTransNodes(TransInfo *t) loc[1] += 0.5f * BLI_rctf_size_y(&node->totr); #endif - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* Weirdo - but the node system is a mix of free 2d elements and DPI sensitive UI. */ loc[0] /= dpi_fac; loc[1] /= dpi_fac; diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 5964af7ed4b..3735ff0727c 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -91,8 +91,8 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_count, int *r_flag) /* *** Extend Transform *** */ int cfra = CFRA; - int left = SEQ_transform_get_left_handle_frame(seq); - int right = SEQ_transform_get_right_handle_frame(seq); + int left = SEQ_time_left_handle_frame_get(seq); + int right = SEQ_time_right_handle_frame_get(seq); if (((seq->flag & SELECT) == 0 || SEQ_transform_is_locked(channels, seq))) { *r_count = 0; @@ -173,16 +173,16 @@ static TransData *SeqToTransData( /* Use seq_tx_get_final_left() and an offset here * so transform has the left hand location of the strip. * tdsq->start_offset is used when flushing the tx data back */ - start_left = SEQ_transform_get_left_handle_frame(seq); + start_left = SEQ_time_left_handle_frame_get(seq); td2d->loc[0] = start_left; tdsq->start_offset = start_left - seq->start; /* use to apply the original location */ break; case SEQ_LEFTSEL: - start_left = SEQ_transform_get_left_handle_frame(seq); + start_left = SEQ_time_left_handle_frame_get(seq); td2d->loc[0] = start_left; break; case SEQ_RIGHTSEL: - td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq); + td2d->loc[0] = SEQ_time_right_handle_frame_get(seq); break; } @@ -489,11 +489,11 @@ static void seq_transform_handle_overwrite_trim(Scene *scene, continue; } if (overlap == STRIP_OVERLAP_LEFT_SIDE) { - SEQ_transform_set_left_handle_frame(seq, transformed->enddisp); + SEQ_time_left_handle_frame_set(seq, transformed->enddisp); } else { BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); - SEQ_transform_set_right_handle_frame(seq, transformed->startdisp); + SEQ_time_right_handle_frame_set(seq, transformed->startdisp); } SEQ_time_update_sequence(scene, seqbasep, seq); @@ -915,7 +915,7 @@ static void flushTransSeq(TransInfo *t) } case SEQ_LEFTSEL: { /* No vertical transform. */ int old_startdisp = seq->startdisp; - SEQ_transform_set_left_handle_frame(seq, new_frame); + SEQ_time_left_handle_frame_set(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); SEQ_time_update_sequence(t->scene, seqbasep, seq); @@ -926,7 +926,7 @@ static void flushTransSeq(TransInfo *t) } case SEQ_RIGHTSEL: { /* No vertical transform. */ int old_enddisp = seq->enddisp; - SEQ_transform_set_right_handle_frame(seq, new_frame); + SEQ_time_right_handle_frame_set(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); SEQ_time_update_sequence(t->scene, seqbasep, seq); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c index cbc2cab0a7a..deae51a1149 100644 --- a/source/blender/editors/transform/transform_convert_sequencer_image.c +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -197,11 +197,7 @@ void recalcData_sequencer_image(TransInfo *t) /* Rotation. Scaling can cause negative rotation. */ if (t->mode == TFM_ROTATION) { - const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)}; - float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1]; - transform->rotation = tdseq->orig_rotation + rotation; - transform->rotation += DEG2RAD(360.0); - transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + transform->rotation = tdseq->orig_rotation - t->values_final[0]; } SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); } @@ -209,9 +205,6 @@ void recalcData_sequencer_image(TransInfo *t) void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t) { - if (t->state == TRANS_CANCEL) { - return; - } TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); TransData *td = NULL; @@ -225,8 +218,14 @@ void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo * TransDataSeq *tdseq = td->extra; Sequence *seq = tdseq->seq; StripTransform *transform = seq->strip->transform; - Scene *scene = t->scene; + if (t->state == TRANS_CANCEL) { + if (t->mode == TFM_ROTATION) { + transform->rotation = tdseq->orig_rotation; + } + continue; + } + Scene *scene = t->scene; RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr); if (t->mode == TFM_ROTATION) { diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index bf9f929dd65..d447cd71a40 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -713,23 +713,23 @@ void recalcData_tracking(TransInfo *t) if (t->mode == TFM_TRANSLATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_POS); + BKE_tracking_marker_clamp_search_position(marker); } } else if (t->mode == TFM_RESIZE) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(marker); } if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH)) { - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(marker); } } else if (t->mode == TFM_ROTATION) { if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT)) { - BKE_tracking_marker_clamp(marker, CLAMP_PAT_POS); + BKE_tracking_marker_clamp_pattern_position(marker); } } } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index afad4df2c88..769fd28c57b 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -15,6 +15,7 @@ #include "BLI_math.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_state.h" #include "BKE_context.h" @@ -25,6 +26,7 @@ #include "RNA_access.h" +#include "WM_api.h" #include "WM_types.h" #include "ED_gizmo_library.h" @@ -173,20 +175,20 @@ void drawSnapping(const struct bContext *C, TransInfo *t) return; } - UI_GetThemeColor3ubv(TH_TRANSFORM, col); - col[3] = 128; - - UI_GetThemeColor3ubv(TH_SELECT, selectedCol); - selectedCol[3] = 128; - - UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); - activeCol[3] = 192; - if (t->spacetype == SPACE_VIEW3D) { bool draw_target = (t->tsnap.status & TARGET_INIT) && (t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR); if (draw_target || validSnap(t)) { + UI_GetThemeColor3ubv(TH_TRANSFORM, col); + col[3] = 128; + + UI_GetThemeColor3ubv(TH_SELECT, selectedCol); + selectedCol[3] = 128; + + UI_GetThemeColor3ubv(TH_ACTIVE, activeCol); + activeCol[3] = 192; + const float *loc_cur = NULL; const float *loc_prev = NULL; const float *normal = NULL; @@ -240,8 +242,26 @@ void drawSnapping(const struct bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_IMAGE) { if (validSnap(t)) { - /* This will not draw, and I'm nor sure why - campbell */ - /* TODO: see 2.7x for non-working code */ + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + float x, y; + const float snap_point[2] = { + t->tsnap.snapPoint[0] / t->aspect[0], + t->tsnap.snapPoint[1] / t->aspect[1], + }; + UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y); + float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize; + + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(t->region); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3ub(255, 255, 255); + imm_draw_circle_wire_2d(pos, x, y, radius, 8); + immUnbindProgram(); + + GPU_matrix_pop_projection(); } } else if (t->spacetype == SPACE_NODE) { @@ -990,17 +1010,19 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec)) { BLI_assert(t->spacetype == SPACE_IMAGE); if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) { - float co[2]; - - UI_view2d_region_to_view(&t->region->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); - uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( t->view_layer, NULL, &objects_len); - float dist_sq = FLT_MAX; - if (ED_uvedit_nearest_uv_multi( - t->scene, objects, objects_len, co, &dist_sq, t->tsnap.snapPoint)) { + float dist_sq = square_f((float)SNAP_MIN_DISTANCE); + if (ED_uvedit_nearest_uv_multi(&t->region->v2d, + t->scene, + objects, + objects_len, + t->mval, + t->tsnap.modeSelect == SNAP_NOT_SELECTED, + &dist_sq, + t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; t->tsnap.snapPoint[1] *= t->aspect[1]; diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index e7b3bce700e..3cfe6bd61a4 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -41,6 +41,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_paint.h" +#include "ED_screen.h" #include "ED_space_api.h" #include "ED_util.h" @@ -187,6 +188,18 @@ void ED_editors_init(bContext *C) ED_space_image_paint_update(bmain, wm, scene); } + /* Enforce a full redraw for the first time areas/regions get drawn. Further region init/refresh + * just triggers non-rebuild redraws (#RGN_DRAW_NO_REBUILD). Usually a full redraw would be + * triggered by a `NC_WM | ND_FILEREAD` notifier, but if a startup script calls an operator that + * redraws the window, notifiers are not handled before the operator runs. See T98461. */ + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = WM_window_get_active_screen(win); + + ED_screen_areas_iter (win, screen, area) { + ED_area_tag_redraw(area); + } + } + ED_assetlist_storage_tag_main_data_dirty(); SWAP(int, reports->flag, reports_flag_prev); diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 803e65590a0..af3589e50f0 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -67,19 +67,19 @@ static bool lib_id_preview_editing_poll(bContext *C) static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) { - char path[FILE_MAX]; + char filepath[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", path); + RNA_string_get(op->ptr, "filepath", filepath); - if (!BLI_is_file(path)) { - BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + if (!BLI_is_file(filepath)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath); return OPERATOR_CANCELLED; } PointerRNA idptr = CTX_data_pointer_get(C, "id"); ID *id = (ID *)idptr.data; - BKE_previewimg_id_custom_set(id, path); + BKE_previewimg_id_custom_set(id, filepath); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 6405d2df66a..ead017a91bf 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1022,8 +1022,13 @@ bool uv_find_nearest_vert_multi(Scene *scene, return found; } -bool ED_uvedit_nearest_uv( - const Scene *scene, Object *obedit, const float co[2], float *dist_sq, float r_uv[2]) +static bool uvedit_nearest_uv(const Scene *scene, + Object *obedit, + const float co[2], + const float scale[2], + const bool ignore_selected, + float *dist_sq, + float r_uv[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); BMIter iter; @@ -1038,8 +1043,14 @@ bool ED_uvedit_nearest_uv( BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(efa); do { + if (ignore_selected && uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) { + continue; + } + const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv; - const float dist_test = len_squared_v2v2(co, uv); + float co_tmp[2]; + mul_v2_v2v2(co_tmp, scale, uv); + const float dist_test = len_squared_v2v2(co, co_tmp); if (dist_best > dist_test) { dist_best = dist_test; uv_best = uv; @@ -1055,17 +1066,27 @@ bool ED_uvedit_nearest_uv( return false; } -bool ED_uvedit_nearest_uv_multi(const Scene *scene, +bool ED_uvedit_nearest_uv_multi(const View2D *v2d, + const Scene *scene, Object **objects, const uint objects_len, - const float co[2], + const int mval[2], + const bool ignore_selected, float *dist_sq, float r_uv[2]) { bool found = false; + + float scale[2], offset[2]; + UI_view2d_scale_get(v2d, &scale[0], &scale[1]); + UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]); + + float co[2]; + sub_v2_v2v2(co, (float[2]){UNPACK2(mval)}, offset); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - if (ED_uvedit_nearest_uv(scene, obedit, co, dist_sq, r_uv)) { + if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) { found = true; } } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index c0ea753ed51..84dca352c9f 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -103,7 +103,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit) int cd_loop_uv_offset; if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { - ED_mesh_uv_texture_add(obedit->data, NULL, true, true, NULL); + ED_mesh_uv_add(obedit->data, NULL, true, true, NULL); } /* Happens when there are no faces. */ @@ -339,7 +339,6 @@ static ParamHandle *construct_param_handle(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; BMFace *efa; BMLoop *l; BMEdge *eed; @@ -348,7 +347,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -417,14 +416,13 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, const UnwrapOptions *options, int *count_fail) { - ParamHandle *handle; BMFace *efa; BMLoop *l; BMEdge *eed; BMIter iter, liter; int i; - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { Object *ob = objects[0]; @@ -539,7 +537,6 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const UnwrapOptions *options, UnwrapResultInfo *result_info) { - ParamHandle *handle; /* index pointers */ MPoly *mpoly; MLoop *mloop; @@ -571,7 +568,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - handle = GEO_uv_parametrizer_construct_begin(); + ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); if (options->correct_aspect) { float aspx, aspy; @@ -1023,8 +1020,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm) bool rotate = true; bool ignore_pinned = false; - ParamHandle *handle; - handle = construct_param_handle(scene, ob, bm, &options, NULL); + ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1043,8 +1039,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, bool rotate, bool ignore_pinned) { - ParamHandle *handle; - handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL); GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -3038,7 +3033,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) * since we are not in edit mode we need to ensure only the uv flags are tested */ scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION; - ED_mesh_uv_texture_ensure(me, NULL); + ED_mesh_uv_ensure(me, NULL); BM_mesh_bm_from_me(bm, me, diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index 9304e6aa3fb..e76e74b89e4 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -370,7 +370,7 @@ int BlenderFileLoader::testDegenerateTriangle(float v1[3], float v2[3], float v3 return 0; } -static bool testEdgeMark(Mesh *me, FreestyleEdge *fed, const MLoopTri *lt, int i) +static bool testEdgeMark(Mesh *me, const FreestyleEdge *fed, const MLoopTri *lt, int i) { MLoop *mloop = &me->mloop[lt->tri[i]]; MLoop *mloop_next = &me->mloop[lt->tri[(i + 1) % 3]]; @@ -395,7 +395,7 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) // Compute loop normals BKE_mesh_calc_normals_split(me); - float(*lnors)[3] = nullptr; + const float(*lnors)[3] = nullptr; if (CustomData_has_layer(&me->ldata, CD_NORMAL)) { lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL); @@ -405,8 +405,8 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id) MVert *mvert = me->mvert; MLoop *mloop = me->mloop; MPoly *mpoly = me->mpoly; - FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE); - FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE); + const FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE); + const FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE); // Compute view matrix Object *ob_camera_eval = DEG_get_evaluated_object(_depsgraph, RE_GetCamera(_re)); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 03e9134c6c2..979673fd736 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -499,9 +499,9 @@ void BlenderStrokeRenderer::test_strip_visibility(Strip::vertex_container &strip StrokeVertexRep *svRep[3]; bool visible; - // iterate over all vertices and count visible faces and strip segments - // (note: a strip segment is a series of visible faces, while two strip - // segments are separated by one or more invisible faces) + /* Iterate over all vertices and count visible faces and strip segments + * (NOTE: a strip segment is a series of visible faces, while two strip + * segments are separated by one or more invisible faces). */ v[0] = strip_vertices.begin(); v[1] = v[0] + 1; v[2] = v[0] + 2; diff --git a/source/blender/freestyle/intern/geometry/SweepLine.h b/source/blender/freestyle/intern/geometry/SweepLine.h index 1165e1bf064..c170ee4d122 100644 --- a/source/blender/freestyle/intern/geometry/SweepLine.h +++ b/source/blender/freestyle/intern/geometry/SweepLine.h @@ -183,7 +183,7 @@ template<class T1, class T2> struct binary_rule { binary_rule() { } - template<class T3, class T4> binary_rule(const binary_rule<T3, T4> &brother) + template<class T3, class T4> binary_rule(const binary_rule<T3, T4> & /*brother*/) { } virtual ~binary_rule() diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 0005d788a3b..46051a58e8b 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -372,7 +372,7 @@ class FieldEvaluator : NonMovable, NonCopyable { /** * \param field: Field to add to the evaluator. * \param dst: Mutable span that the evaluated result for this field is be written into. - * \note: When the output may only be used as a single value, the version of this function with + * \note When the output may only be used as a single value, the version of this function with * a virtual array result array should be used. */ int add_with_destination(GField field, GMutableSpan dst); @@ -380,7 +380,7 @@ class FieldEvaluator : NonMovable, NonCopyable { /** * \param field: Field to add to the evaluator. * \param dst: Mutable span that the evaluated result for this field is be written into. - * \note: When the output may only be used as a single value, the version of this function with + * \note When the output may only be used as a single value, the version of this function with * a virtual array result array should be used. */ template<typename T> int add_with_destination(Field<T> field, MutableSpan<T> dst) diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 9d09378ab63..16a33c9cda7 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -41,6 +41,10 @@ class MFParamsBuilder { MFParamsBuilder(const MFSignature &signature, const IndexMask mask) : signature_(&signature), mask_(mask), min_array_size_(mask.min_array_size()) { + virtual_arrays_.reserve(signature.virtual_array_num); + mutable_spans_.reserve(signature.span_num); + virtual_vector_arrays_.reserve(signature.virtual_vector_array_num); + vector_arrays_.reserve(signature.vector_array_num); } public: @@ -53,28 +57,33 @@ class MFParamsBuilder { template<typename T> void add_readonly_single_input_value(T value, StringRef expected_name = "") { - this->add_readonly_single_input(VArray<T>::ForSingle(std::move(value), min_array_size_), - expected_name); + this->assert_current_param_type(MFParamType::ForSingleInput(CPPType::get<T>()), expected_name); + virtual_arrays_.append_unchecked_as( + varray_tag::single{}, CPPType::get<T>(), min_array_size_, &value); } template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input( - GVArray::ForSingleRef(CPPType::get<T>(), min_array_size_, value), expected_name); + this->assert_current_param_type(MFParamType::ForSingleInput(CPPType::get<T>()), expected_name); + virtual_arrays_.append_unchecked_as( + varray_tag::single_ref{}, CPPType::get<T>(), min_array_size_, value); } void add_readonly_single_input(const GSpan span, StringRef expected_name = "") { - this->add_readonly_single_input(GVArray::ForSpan(span), expected_name); + this->assert_current_param_type(MFParamType::ForSingleInput(span.type()), expected_name); + BLI_assert(span.size() >= min_array_size_); + virtual_arrays_.append_unchecked_as(varray_tag::span{}, span); } void add_readonly_single_input(GPointer value, StringRef expected_name = "") { - this->add_readonly_single_input( - GVArray::ForSingleRef(*value.type(), min_array_size_, value.get()), expected_name); + this->assert_current_param_type(MFParamType::ForSingleInput(*value.type()), expected_name); + virtual_arrays_.append_unchecked_as( + varray_tag::single_ref{}, *value.type(), min_array_size_, value.get()); } void add_readonly_single_input(GVArray varray, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForSingleInput(varray.type()), expected_name); BLI_assert(varray.size() >= min_array_size_); - virtual_arrays_.append(varray); + virtual_arrays_.append_unchecked_as(std::move(varray)); } void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") @@ -92,7 +101,7 @@ class MFParamsBuilder { { this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); - virtual_vector_arrays_.append(&ref); + virtual_vector_arrays_.append_unchecked(&ref); } template<typename T> void add_uninitialized_single_output(T *value, StringRef expected_name = "") @@ -104,7 +113,7 @@ class MFParamsBuilder { { this->assert_current_param_type(MFParamType::ForSingleOutput(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); - mutable_spans_.append(ref); + mutable_spans_.append_unchecked(ref); } void add_ignored_single_output(StringRef expected_name = "") { @@ -115,7 +124,7 @@ class MFParamsBuilder { const CPPType &type = param_type.data_type().single_type(); /* An empty span indicates that this is ignored. */ const GMutableSpan dummy_span{type}; - mutable_spans_.append(dummy_span); + mutable_spans_.append_unchecked(dummy_span); } void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") @@ -123,14 +132,14 @@ class MFParamsBuilder { this->assert_current_param_type(MFParamType::ForVectorOutput(vector_array.type()), expected_name); BLI_assert(vector_array.size() >= min_array_size_); - vector_arrays_.append(&vector_array); + vector_arrays_.append_unchecked(&vector_array); } void add_single_mutable(GMutableSpan ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForMutableSingle(ref.type()), expected_name); BLI_assert(ref.size() >= min_array_size_); - mutable_spans_.append(ref); + mutable_spans_.append_unchecked(ref); } void add_vector_mutable(GVectorArray &vector_array, StringRef expected_name = "") @@ -138,7 +147,7 @@ class MFParamsBuilder { this->assert_current_param_type(MFParamType::ForMutableVector(vector_array.type()), expected_name); BLI_assert(vector_array.size() >= min_array_size_); - vector_arrays_.append(&vector_array); + vector_arrays_.append_unchecked(&vector_array); } GMutableSpan computed_array(int param_index) diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index 62c491609a4..6181555dbd1 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -29,6 +29,15 @@ struct MFSignature { Vector<int> param_data_indices; bool depends_on_context = false; + /** + * Number of elements of each of these types that has to be passed into the multi-function as an + * input or output. + */ + int span_num = 0; + int virtual_array_num = 0; + int virtual_vector_array_num = 0; + int vector_array_num = 0; + int data_index(int param_index) const { return param_data_indices[param_index]; @@ -38,10 +47,6 @@ struct MFSignature { class MFSignatureBuilder { private: MFSignature signature_; - int span_count_ = 0; - int virtual_array_count_ = 0; - int virtual_vector_array_count_ = 0; - int vector_array_count_ = 0; public: MFSignatureBuilder(const char *function_name) @@ -79,10 +84,10 @@ class MFSignatureBuilder { switch (data_type.category()) { case MFDataType::Single: - signature_.param_data_indices.append(virtual_array_count_++); + signature_.param_data_indices.append(signature_.virtual_array_num++); break; case MFDataType::Vector: - signature_.param_data_indices.append(virtual_vector_array_count_++); + signature_.param_data_indices.append(signature_.virtual_vector_array_num++); break; } } @@ -112,10 +117,10 @@ class MFSignatureBuilder { switch (data_type.category()) { case MFDataType::Single: - signature_.param_data_indices.append(span_count_++); + signature_.param_data_indices.append(signature_.span_num++); break; case MFDataType::Vector: - signature_.param_data_indices.append(vector_array_count_++); + signature_.param_data_indices.append(signature_.vector_array_num++); break; } } @@ -145,10 +150,10 @@ class MFSignatureBuilder { switch (data_type.category()) { case MFDataType::Single: - signature_.param_data_indices.append(span_count_++); + signature_.param_data_indices.append(signature_.span_num++); break; case MFDataType::Vector: - signature_.param_data_indices.append(vector_array_count_++); + signature_.param_data_indices.append(signature_.vector_array_num++); break; } } diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 8716d6c8f67..010c327d482 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,15 +16,19 @@ set(INC set(SRC intern/mesh_merge_by_distance.cc + intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc intern/point_merge_by_distance.cc intern/realize_instances.cc + intern/resample_curves.cc intern/uv_parametrizer.c GEO_mesh_merge_by_distance.hh + GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh GEO_point_merge_by_distance.hh GEO_realize_instances.hh + GEO_resample_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_mesh_primitive_cuboid.hh b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh new file mode 100644 index 00000000000..6107b15b62d --- /dev/null +++ b/source/blender/geometry/GEO_mesh_primitive_cuboid.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" + +struct Mesh; +namespace blender { +namespace bke { +class AttributeIDRef; +} +} // namespace blender + +namespace blender::geometry { + +Mesh *create_cuboid_mesh( + const float3 &size, int verts_x, int verts_y, int verts_z, const bke::AttributeIDRef &uv_id); + +Mesh *create_cuboid_mesh(const float3 &size, int verts_x, int verts_y, int verts_z); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh new file mode 100644 index 00000000000..97399ccb0a5 --- /dev/null +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field.hh" + +#include "BKE_geometry_set.hh" + +struct Curves; + +namespace blender::geometry { + +/** + * Create new curves where the selected curves have been resampled with a number of uniform-length + * samples defined by the count field. Interpolate attributes to the result, with an accuracy that + * depends on the curve's resolution parameter. + * + * \note The values provided by the #count_field are clamped to 1 or greater. + */ +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field); + +/** + * Create new curves resampled to make each segment have the length specified by the + * #segment_length field input, rounded to make the length of each segment the same. + * The accuracy will depend on the curve's resolution parameter. + */ +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field); + +/** + * Evaluate each selected curve to its implicit evaluated points. + */ +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index a5194883cf2..624b0695aa3 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -12,8 +12,8 @@ extern "C" { #endif -typedef void ParamHandle; /* handle to a set of charts */ -typedef intptr_t ParamKey; /* (hash) key for identifying verts and faces */ +typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ +typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ typedef enum ParamBool { PARAM_TRUE = 1, PARAM_FALSE = 0, diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index e45b84632ab..d3a5a194555 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -82,7 +82,7 @@ struct WeldPoly { int loop_start; int loop_end; /* Final Polygon Size. */ - int len; + int loop_len; /* Group of loops that will be affected. */ struct WeldGroup loops; }; @@ -104,9 +104,6 @@ struct WeldMesh { /* References all polygons and loops that will be affected. */ Vector<WeldLoop> wloop; Vector<WeldPoly> wpoly; - MutableSpan<WeldPoly> wpoly_new; - int wloop_len; - int wpoly_len; int wpoly_new_len; /* From the actual index of the element in the mesh, it indicates what is the index of the Weld @@ -147,14 +144,14 @@ struct WeldLoopOfPolyIter { * \{ */ #ifdef USE_WELD_DEBUG -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter &iter, const WeldPoly &wp, Span<WeldLoop> wloop, Span<MLoop> mloop, Span<int> loop_map, int *group_buffer); -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter); static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_kill_len) { @@ -170,12 +167,8 @@ static void weld_assert_edge_kill_len(Span<WeldEdge> wedge, const int supposed_k BLI_assert(kills == supposed_kill_len); } -static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, - Span<WeldPoly> wpoly_new, - Span<WeldLoop> wloop, +static void weld_assert_poly_and_loop_kill_len(WeldMesh *weld_mesh, Span<MLoop> mloop, - Span<int> loop_map, - Span<int> poly_map, Span<MPoly> mpoly, const int supposed_poly_kill_len, const int supposed_loop_kill_len) @@ -184,11 +177,12 @@ static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, int loop_kills = mloop.size(); const MPoly *mp = &mpoly[0]; for (int i = 0; i < mpoly.size(); i++, mp++) { - int poly_ctx = poly_map[i]; + int poly_ctx = weld_mesh->poly_map[i]; if (poly_ctx != OUT_OF_CONTEXT) { - const WeldPoly *wp = &wpoly[poly_ctx]; + const WeldPoly *wp = &weld_mesh->wpoly[poly_ctx]; WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, *wp, wloop, mloop, loop_map, nullptr)) { + if (!weld_iter_loop_of_poly_begin( + iter, *wp, weld_mesh->wloop, mloop, weld_mesh->loop_map, nullptr)) { poly_kills++; continue; } @@ -197,13 +191,13 @@ static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, poly_kills++; continue; } - int remain = wp->len; + int remain = wp->loop_len; int l = wp->loop_start; while (remain) { int l_next = l + 1; - int loop_ctx = loop_map[l]; + int loop_ctx = weld_mesh->loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; + const WeldLoop *wl = &weld_mesh->wloop[loop_ctx]; if (wl->loop_skip_to != OUT_OF_CONTEXT) { l_next = wl->loop_skip_to; } @@ -225,19 +219,19 @@ static void weld_assert_poly_and_loop_kill_len(Span<WeldPoly> wpoly, } } - const WeldPoly *wp = wpoly_new.data(); - for (int i = wpoly_new.size(); i--; wp++) { - if (wp->poly_dst != OUT_OF_CONTEXT) { + for (const int i : weld_mesh->wpoly.index_range().take_back(weld_mesh->wpoly_new_len)) { + const WeldPoly &wp = weld_mesh->wpoly[i]; + if (wp.poly_dst != OUT_OF_CONTEXT) { poly_kills++; continue; } - int remain = wp->len; - int l = wp->loop_start; + int remain = wp.loop_len; + int l = wp.loop_start; while (remain) { int l_next = l + 1; - int loop_ctx = loop_map[l]; + int loop_ctx = weld_mesh->loop_map[l]; if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; + const WeldLoop *wl = &weld_mesh->wloop[loop_ctx]; if (wl->loop_skip_to != OUT_OF_CONTEXT) { l_next = wl->loop_skip_to; } @@ -263,10 +257,10 @@ static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, Span<MLoop> mloop, Span<int> loop_map) { - const int len = wp.len; - Array<int, 64> verts(len); + const int loop_len = wp.loop_len; + Array<int, 64> verts(loop_len); WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, nullptr)) { + if (!weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { return; } else { @@ -275,9 +269,9 @@ static void weld_assert_poly_no_vert_repetition(const WeldPoly &wp, verts[i++] = iter.v; } } - for (int i = 0; i < len; i++) { + for (int i = 0; i < loop_len; i++) { int va = verts[i]; - for (int j = i + 1; j < len; j++) { + for (int j = i + 1; j < loop_len; j++) { int vb = verts[j]; BLI_assert(va != vb); } @@ -290,7 +284,7 @@ static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop) return; } - int len = wp->len; + int loop_len = wp->loop_len; const WeldLoop *wl = &wloop[wp->loops.ofs]; BLI_assert(wp->loop_start <= wl->loop_orig); @@ -304,10 +298,10 @@ static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop) min_len++; } } - BLI_assert(len >= min_len); + BLI_assert(loop_len >= min_len); int max_len = wp->loop_end - wp->loop_start + 1; - BLI_assert(len <= max_len); + BLI_assert(loop_len <= max_len); } #endif /* USE_WELD_DEBUG */ @@ -786,7 +780,7 @@ static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, wp.loops.ofs = prev_wloop_len; wp.loop_start = loopstart; wp.loop_end = loopstart + totloop - 1; - wp.len = totloop; + wp.loop_len = totloop; wpoly.append(wp); poly_map[i] = wpoly_len++; @@ -802,15 +796,10 @@ static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly, } } - if (mpoly.size() < (wpoly_len + maybe_new_poly)) { - wpoly.resize(wpoly_len + maybe_new_poly); - } + wpoly.reserve(wpoly.size() + maybe_new_poly); r_weld_mesh->wloop = std::move(wloop); r_weld_mesh->wpoly = std::move(wpoly); - r_weld_mesh->wpoly_new = r_weld_mesh->wpoly.as_mutable_span().drop_front(wpoly_len); - r_weld_mesh->wloop_len = wloop_len; - r_weld_mesh->wpoly_len = wpoly_len; r_weld_mesh->wpoly_new_len = 0; r_weld_mesh->loop_map = std::move(loop_map); r_weld_mesh->poly_map = std::move(poly_map); @@ -827,8 +816,8 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, int *r_poly_kill, int *r_loop_kill) { - int poly_len = r_wp->len; - if (poly_len < 3 || ctx_verts_len < 1) { + int poly_loop_len = r_wp->loop_len; + if (poly_loop_len < 3 || ctx_verts_len < 1) { return; } @@ -870,7 +859,7 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, } if (vert_a == vert_b) { const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; - const int dist_b = poly_len - dist_a; + const int dist_b = poly_loop_len - dist_a; BLI_assert(dist_a != 0 && dist_b != 0); if (dist_a == 1 || dist_b == 1) { @@ -886,7 +875,7 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, wla->flag = ELEM_COLLAPSED; wl_tmp->flag = ELEM_COLLAPSED; loop_kill += 2; - poly_len -= 2; + poly_loop_len -= 2; } if (dist_b == 2) { if (wl_tmp != nullptr) { @@ -901,20 +890,22 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, wl_tmp->flag = ELEM_COLLAPSED; } loop_kill += 2; - poly_len -= 2; + poly_loop_len -= 2; } if (wl_tmp == nullptr) { const int new_loops_len = lb - la; const int new_loops_ofs = ctx_loops_ofs + la; - WeldPoly *new_wp = &r_weld_mesh->wpoly_new[r_weld_mesh->wpoly_new_len++]; + r_weld_mesh->wpoly.increase_size_by_unchecked(1); + WeldPoly *new_wp = &r_weld_mesh->wpoly.last(); new_wp->poly_dst = OUT_OF_CONTEXT; new_wp->poly_orig = r_wp->poly_orig; new_wp->loops.len = new_loops_len; new_wp->loops.ofs = new_loops_ofs; new_wp->loop_start = wla->loop_orig; new_wp->loop_end = wlb_prev->loop_orig; - new_wp->len = dist_a; + new_wp->loop_len = dist_a; + r_weld_mesh->wpoly_new_len++; weld_poly_split_recursive(vert_dest_map, #ifdef USE_WELD_DEBUG mloop, @@ -924,8 +915,8 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, r_weld_mesh, r_poly_kill, r_loop_kill); - BLI_assert(dist_b == poly_len - dist_a); - poly_len = dist_b; + BLI_assert(dist_b == poly_loop_len - dist_a); + poly_loop_len = dist_b; if (wla_prev->loop_orig > wla->loop_orig) { /* New start. */ r_wp->loop_start = wlb->loop_orig; @@ -950,7 +941,7 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map, wla_prev = wla; } } - r_wp->len = poly_len; + r_wp->loop_len = poly_loop_len; *r_loop_kill += loop_kill; #ifdef USE_WELD_DEBUG @@ -968,10 +959,8 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, MutableSpan<WeldGroup> r_vlinks, WeldMesh *r_weld_mesh) { - MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly; + WeldPoly *wpoly = r_weld_mesh->wpoly.data(); MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop; - int wpoly_len = r_weld_mesh->wpoly_len; - int wpoly_new_len = 0; int poly_kill_len = 0; int loop_kill_len = 0; @@ -979,28 +968,31 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, if (remain_edge_ctx_len) { - /* Setup Poly/Loop. Note that `wpoly_len` may be different than `wpoly.size()` here. */ - for (const int i : IndexRange(wpoly_len)) { + /* Setup Poly/Loop. */ + /* `wpoly.size()` may change during the loop, + * so make it clear that we are only working with the original wpolys. */ + IndexRange wpoly_original_range = r_weld_mesh->wpoly.index_range(); + for (const int i : wpoly_original_range) { WeldPoly &wp = wpoly[i]; const int ctx_loops_len = wp.loops.len; const int ctx_loops_ofs = wp.loops.ofs; - int poly_len = wp.len; + int poly_loop_len = wp.loop_len; int ctx_verts_len = 0; WeldLoop *wl = &wloop[ctx_loops_ofs]; for (int l = ctx_loops_len; l--; wl++) { const int edge_dest = wl->edge; if (edge_dest == ELEM_COLLAPSED) { wl->flag = ELEM_COLLAPSED; - if (poly_len == 3) { + if (poly_loop_len == 3) { wp.flag = ELEM_COLLAPSED; poly_kill_len++; loop_kill_len += 3; - poly_len = 0; + poly_loop_len = 0; break; } loop_kill_len++; - poly_len--; + poly_loop_len--; } else { const int vert_dst = wl->vert; @@ -1010,10 +1002,10 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, } } - if (poly_len) { - wp.len = poly_len; + if (poly_loop_len) { + wp.loop_len = poly_loop_len; #ifdef USE_WELD_DEBUG - weld_assert_poly_len(wp, wloop); + weld_assert_poly_len(&wp, wloop); #endif weld_poly_split_recursive(vert_dest_map, @@ -1025,32 +1017,19 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, r_weld_mesh, &poly_kill_len, &loop_kill_len); - - wpoly_new_len = r_weld_mesh->wpoly_new_len; } } #ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - r_weld_mesh->wpoly_new, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - poly_kill_len, - loop_kill_len); + weld_assert_poly_and_loop_kill_len(r_weld_mesh, mloop, mpoly, poly_kill_len, loop_kill_len); #endif /* Setup Polygon Overlap. */ - const int wpoly_and_new_len = wpoly_len + wpoly_new_len; - r_vlinks.fill({0, 0}); MutableSpan<WeldGroup> v_links = r_vlinks; - for (const int i : IndexRange(wpoly_and_new_len)) { - const WeldPoly &wp = wpoly[i]; + for (const WeldPoly &wp : r_weld_mesh->wpoly) { WeldLoopOfPolyIter iter; if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { while (weld_iter_loop_of_poly_next(iter)) { @@ -1068,7 +1047,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, if (link_len) { Array<int> link_poly_buffer(link_len); - for (const int i : IndexRange(wpoly_and_new_len)) { + for (const int i : IndexRange(r_weld_mesh->wpoly.size())) { const WeldPoly &wp = wpoly[i]; WeldLoopOfPolyIter iter; if (weld_iter_loop_of_poly_begin(iter, wp, wloop, mloop, loop_map, nullptr)) { @@ -1086,7 +1065,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, int polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; polys_len_b = p_ctx_b = 0; /* silence warnings */ - for (const int i : IndexRange(wpoly_and_new_len)) { + for (const int i : IndexRange(r_weld_mesh->wpoly.size())) { const WeldPoly &wp = wpoly[i]; if (wp.poly_dst != OUT_OF_CONTEXT) { /* No need to retest poly. @@ -1103,7 +1082,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, BLI_assert(link_poly_buffer[link_a->ofs] == i); continue; } - int wp_len = wp.len; + int wp_loop_len = wp.loop_len; polys_ctx_a = &link_poly_buffer[link_a->ofs]; for (; polys_len_a--; polys_ctx_a++) { p_ctx_a = *polys_ctx_a; @@ -1112,7 +1091,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, } WeldPoly *wp_tmp = &wpoly[p_ctx_a]; - if (wp_tmp->len != wp_len) { + if (wp_tmp->loop_len != wp_loop_len) { continue; } @@ -1151,31 +1130,23 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop, BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); BLI_assert(wp_tmp != &wp); wp_tmp->poly_dst = wp.poly_orig; - loop_kill_len += wp_tmp->len; + loop_kill_len += wp_tmp->loop_len; poly_kill_len++; } } } } else { - poly_kill_len = r_weld_mesh->wpoly_len; - loop_kill_len = r_weld_mesh->wloop_len; + poly_kill_len = r_weld_mesh->wpoly.size(); + loop_kill_len = r_weld_mesh->wloop.size(); - for (WeldPoly &wp : wpoly) { + for (WeldPoly &wp : r_weld_mesh->wpoly) { wp.flag = ELEM_COLLAPSED; } } #ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - r_weld_mesh->wpoly_new, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - poly_kill_len, - loop_kill_len); + weld_assert_poly_and_loop_kill_len(r_weld_mesh, mloop, mpoly, poly_kill_len, loop_kill_len); #endif r_weld_mesh->poly_kill_len = poly_kill_len; @@ -1545,8 +1516,9 @@ static Mesh *create_merged_mesh(const Mesh &mesh, r_i++; } - for (const int i : IndexRange(weld_mesh.wpoly_new_len)) { - const WeldPoly &wp = weld_mesh.wpoly_new[i]; + /* New Polygons. */ + for (const int i : weld_mesh.wpoly.index_range().take_back(weld_mesh.wpoly_new_len)) { + const WeldPoly &wp = weld_mesh.wpoly[i]; const int loop_start = loop_cur; WeldLoopOfPolyIter iter; if (!weld_iter_loop_of_poly_begin( diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc new file mode 100644 index 00000000000..07ac2419ad9 --- /dev/null +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -0,0 +1,431 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_index_range.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_geometry_set.hh" +#include "BKE_mesh.h" + +#include "GEO_mesh_primitive_cuboid.hh" + +namespace blender::geometry { + +struct CuboidConfig { + float3 size; + int verts_x; + int verts_y; + int verts_z; + int edges_x; + int edges_y; + int edges_z; + int vertex_count; + int poly_count; + int loop_count; + + CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) + : size(size), + verts_x(verts_x), + verts_y(verts_y), + verts_z(verts_z), + edges_x(verts_x - 1), + edges_y(verts_y - 1), + edges_z(verts_z - 1) + { + BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); + this->vertex_count = this->get_vertex_count(); + this->poly_count = this->get_poly_count(); + this->loop_count = this->poly_count * 4; + } + + private: + int get_vertex_count() + { + const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); + return verts_x * verts_y * verts_z - inner_position_count; + } + + int get_poly_count() + { + return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); + } +}; + +static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) +{ + const float z_bottom = -config.size.z / 2.0f; + const float z_delta = config.size.z / config.edges_z; + + const float x_left = -config.size.x / 2.0f; + const float x_delta = config.size.x / config.edges_x; + + const float y_front = -config.size.y / 2.0f; + const float y_delta = config.size.y / config.edges_y; + + int vert_index = 0; + + for (const int z : IndexRange(config.verts_z)) { + if (ELEM(z, 0, config.edges_z)) { + /* Fill bottom and top. */ + const float z_pos = z_bottom + z_delta * z; + for (const int y : IndexRange(config.verts_y)) { + const float y_pos = y_front + y_delta * y; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + } + else { + for (const int y : IndexRange(config.verts_y)) { + if (ELEM(y, 0, config.edges_y)) { + /* Fill y-sides. */ + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + else { + /* Fill x-sides. */ + const float x_pos = x_left; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + const float x_pos2 = x_left + x_delta * config.edges_x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); + } + } + } + } +} + +/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. + * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for + * anti-clockwise. + */ +static void define_quad(MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops, + const int poly_index, + const int loop_index, + const int vert_1, + const int vert_2, + const int vert_3, + const int vert_4) +{ + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_1 = loops[loop_index]; + loop_1.v = vert_1; + MLoop &loop_2 = loops[loop_index + 1]; + loop_2.v = vert_2; + MLoop &loop_3 = loops[loop_index + 2]; + loop_3.v = vert_3; + MLoop &loop_4 = loops[loop_index + 3]; + loop_4.v = vert_4; +} + +static void calculate_polys(const CuboidConfig &config, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) +{ + int loop_index = 0; + int poly_index = 0; + + /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ + const int xy_cross_section_vert_count = config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2); + + /* Calculate polys for Bottom faces. */ + int vert_1_start = 0; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + const int vert_1 = vert_1_start + x; + const int vert_2 = vert_1_start + config.verts_x + x; + const int vert_3 = vert_2 + 1; + const int vert_4 = vert_1 + 1; + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + vert_1_start += config.verts_x; + } + + /* Calculate polys for Front faces. */ + vert_1_start = 0; + int vert_2_start = config.verts_x * config.verts_y; + + for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_1_start = vert_2_start; + vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); + } + + /* Calculate polys for Top faces. */ + vert_1_start = config.verts_x * config.verts_y + + (config.verts_z - 2) * (config.verts_x * config.verts_y - + (config.verts_x - 2) * (config.verts_y - 2)); + vert_2_start = vert_1_start + config.verts_x; + + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_1_start + x + 1, + vert_2_start + x + 1, + vert_2_start + x); + loop_index += 4; + poly_index++; + } + vert_2_start += config.verts_x; + vert_1_start += config.verts_x; + } + + /* Calculate polys for Back faces. */ + vert_1_start = config.verts_x * config.edges_y; + vert_2_start = vert_1_start + xy_cross_section_vert_count; + + for (const int z : IndexRange(config.edges_z)) { + if (z == (config.edges_z - 1)) { + vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); + } + for (const int x : IndexRange(config.edges_x)) { + define_quad(polys, + loops, + poly_index, + loop_index, + vert_1_start + x, + vert_2_start + x, + vert_2_start + x + 1, + vert_1_start + x + 1); + loop_index += 4; + poly_index++; + } + vert_2_start += xy_cross_section_vert_count; + vert_1_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Left faces. */ + vert_1_start = 0; + vert_2_start = config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1; + int vert_2; + int vert_3; + int vert_4; + + if (z == 0 || y == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_1 += config.verts_x - 2; + vert_4 = vert_1 + 2; + } + + if (y == 0 || z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_2 += config.verts_x - 2; + vert_3 = vert_2 + 2; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } + + /* Calculate polys for Right faces. */ + vert_1_start = config.edges_x; + vert_2_start = vert_1_start + config.verts_x * config.verts_y; + + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + int vert_1 = vert_1_start; + int vert_2 = vert_2_start; + int vert_3 = vert_2_start + 2; + int vert_4 = vert_1 + config.verts_x; + + if (z == 0) { + vert_1 = vert_1_start + config.verts_x * y; + vert_4 = vert_1 + config.verts_x; + } + else { + vert_1 = vert_1_start + 2 * y; + vert_4 = vert_1 + 2; + } + + if (z == (config.edges_z - 1)) { + vert_2 = vert_2_start + config.verts_x * y; + vert_3 = vert_2 + config.verts_x; + } + else { + vert_2 = vert_2_start + 2 * y; + vert_3 = vert_2 + 2; + } + + if (y == (config.edges_y - 1)) { + vert_3 = vert_2 + config.verts_x; + vert_4 = vert_1 + config.verts_x; + } + + define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); + loop_index += 4; + poly_index++; + } + if (z == 0) { + vert_1_start += config.verts_x * config.verts_y; + } + else { + vert_1_start += xy_cross_section_vert_count; + } + vert_2_start += xy_cross_section_vert_count; + } +} + +static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + bke::OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>(uv_id, ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); + + int loop_index = 0; + + const float x_delta = 0.25f / static_cast<float>(config.edges_x); + const float y_delta = 0.25f / static_cast<float>(config.edges_y); + const float z_delta = 0.25f / static_cast<float>(config.edges_z); + + /* Calculate bottom face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); + } + } + + /* Calculate front face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); + } + } + + /* Calculate top face UVs. */ + for (const int y : IndexRange(config.edges_y)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); + uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); + uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); + } + } + + /* Calculate back face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int x : IndexRange(config.edges_x)) { + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); + } + } + + /* Calculate left face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); + } + } + + /* Calculate right face UVs. */ + for (const int z : IndexRange(config.edges_z)) { + for (const int y : IndexRange(config.edges_y)) { + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); + uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); + uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); + } + } + + uv_attribute.save(); +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z, + const bke::AttributeIDRef &uv_id) +{ + const CuboidConfig config(size, verts_x, verts_y, verts_z); + + Mesh *mesh = BKE_mesh_new_nomain( + config.vertex_count, 0, 0, config.loop_count, config.poly_count); + + calculate_vertices(config, {mesh->mvert, mesh->totvert}); + + calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); + BKE_mesh_calc_edges(mesh, false, false); + + if (uv_id) { + calculate_uvs(config, mesh, uv_id); + } + + return mesh; +} + +Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + return create_cuboid_mesh(size, verts_x, verts_y, verts_z, {}); +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 09ca83ef976..6639ff650d3 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -97,9 +97,9 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const int merge_index = merge_indices[i]; const int dst_index = src_to_dst_indices[merge_index]; - const IndexRange point_range(map_offsets[dst_index], - map_offsets[dst_index + 1] - map_offsets[dst_index]); - MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(point_range); + const IndexRange points(map_offsets[dst_index], + map_offsets[dst_index + 1] - map_offsets[dst_index]); + MutableSpan<int> point_merge_indices = merge_map.as_mutable_span().slice(points); point_merge_indices[point_merge_counts[dst_index]] = i; point_merge_counts[dst_index]++; } @@ -116,9 +116,8 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, threading::parallel_for(IndexRange(dst_size), 1024, [&](IndexRange range) { for (const int i_dst : range) { - const IndexRange point_range(map_offsets[i_dst], - map_offsets[i_dst + 1] - map_offsets[i_dst]); - dst_ids[i_dst] = src_ids[point_range.first()]; + const IndexRange points(map_offsets[i_dst], map_offsets[i_dst + 1] - map_offsets[i_dst]); + dst_ids[i_dst] = src_ids[points.first()]; } }); @@ -147,9 +146,9 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, * in the mixer the size of the result point cloud and to improve memory locality. */ attribute_math::DefaultMixer<T> mixer{dst.slice(i_dst, 1)}; - const IndexRange point_range(map_offsets[i_dst], - map_offsets[i_dst + 1] - map_offsets[i_dst]); - Span<int> src_merge_indices = merge_map.as_span().slice(point_range); + const IndexRange points(map_offsets[i_dst], + map_offsets[i_dst + 1] - map_offsets[i_dst]); + Span<int> src_merge_indices = merge_map.as_span().slice(points); for (const int i_src : src_merge_indices) { mixer.mix_in(0, src[i_src]); } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index f3f0e5b1fce..ae07e817c67 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -374,7 +374,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_amount()); + to_type, instances_component.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -548,7 +548,7 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); const Curves *curves = curve_component.get_for_read(); - if (curves != nullptr && curves->geometry.curve_size > 0) { + if (curves != nullptr && curves->geometry.curve_num > 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.curves_offsets, @@ -556,8 +556,8 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; - gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_num; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_num; } break; } @@ -1052,7 +1052,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, VectorSet<const Curves *> &r_curves) { if (const Curves *curves = geometry_set.get_curves_for_read()) { - if (curves->geometry.curve_size != 0) { + if (curves->geometry.curve_num != 0) { r_curves.add(curves); } } @@ -1215,13 +1215,13 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; - const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; + const int points_num = last_task.start_indices.point + last_curves.geometry.point_num; + const int curves_num = last_task.start_indices.curve + last_curves.geometry.curve_num; /* Allocate new curves data-block. */ - Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + Curves *dst_curves_id = bke::curves_new_nomain(points_num, curves_num); bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - dst_curves.offsets_for_write().last() = points_size; + dst_curves.offsets_for_write().last() = points_num; CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); dst_component.replace(dst_curves_id); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc new file mode 100644 index 00000000000..7895225a189 --- /dev/null +++ b/source/blender/geometry/intern/resample_curves.cc @@ -0,0 +1,474 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_length_parameterize.hh" +#include "BLI_task.hh" + +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_fields.hh" + +#include "GEO_resample_curves.hh" + +namespace blender::geometry { + +static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field) +{ + static fn::CustomMF_SI_SO<int, int> max_one_fn( + "Clamp Above One", + [](int value) { return std::max(1, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); + auto clamp_op = std::make_shared<fn::FieldOperation>( + fn::FieldOperation(max_one_fn, {count_field})); + + return fn::Field<int>(std::move(clamp_op)); +} + +static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field) +{ + static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( + "Length Input to Count", + [](const float curve_length, const float sample_length) { + /* Find the number of sampled segments by dividing the total length by + * the sample length. Then there is one more sampled point than segment. */ + const int count = int(curve_length / sample_length) + 1; + return std::max(1, count); + }, + fn::CustomMF_presets::AllSpanOrSingle()); + + auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation( + get_count_fn, + {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field})); + + return fn::Field<int>(std::move(get_count_op)); +} + +/** + * Return true if the attribute should be copied/interpolated to the result curves. + * Don't output attributes that correspond to curve types that have no curves in the result. + */ +static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id, + const std::array<int, CURVE_TYPES_NUM> &type_counts) +{ + if (!attribute_id.is_named()) { + return true; + } + if (ELEM(attribute_id.name(), + "handle_type_left", + "handle_type_right", + "handle_left", + "handle_right")) { + return type_counts[CURVE_TYPE_BEZIER] != 0; + } + if (ELEM(attribute_id.name(), "nurbs_weight")) { + return type_counts[CURVE_TYPE_NURBS] != 0; + } + return true; +} + +/** + * Return true if the attribute should be copied to poly curves. + */ +static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attribute_id) +{ + static const Set<StringRef> no_interpolation{{ + "handle_type_left", + "handle_type_right", + "handle_position_right", + "handle_position_left", + "nurbs_weight", + }}; + return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); +} + +/** + * Retrieve spans from source and result attributes. + */ +static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids, + const CurveComponent &src_component, + CurveComponent &dst_component, + Vector<GSpan> &src, + Vector<GMutableSpan> &dst, + Vector<bke::OutputAttribute> &dst_attributes) +{ + for (const int i : ids.index_range()) { + GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); + BLI_assert(src_attribute); + src.append(src_attribute.get_internal_span()); + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); + bke::OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + ids[i], ATTR_DOMAIN_POINT, data_type); + dst.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } +} + +struct AttributesForInterpolation : NonCopyable, NonMovable { + Vector<GSpan> src; + Vector<GMutableSpan> dst; + + Vector<bke::OutputAttribute> dst_attributes; + + Vector<GSpan> src_no_interpolation; + Vector<GMutableSpan> dst_no_interpolation; +}; + +/** + * Gather a set of all generic attribute IDs to copy to the result curves. + */ +static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, + CurveComponent &dst_component, + AttributesForInterpolation &result) +{ + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap( + dst_component.get_for_write()->geometry); + + VectorSet<bke::AttributeIDRef> ids; + VectorSet<bke::AttributeIDRef> ids_no_interpolation; + src_component.attribute_foreach( + [&](const bke::AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.domain != ATTR_DOMAIN_POINT) { + return true; + } + if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { + return true; + } + if (interpolate_attribute_to_poly_curve(id)) { + ids.add_new(id); + } + else { + ids_no_interpolation.add_new(id); + } + return true; + }); + + /* Position is handled differently since it has non-generic interpolation for Bezier + * curves and because the evaluated positions are cached for each evaluated point. */ + ids.remove_contained("position"); + + retrieve_attribute_spans( + ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); + + /* Attributes that aren't interpolated like Bezier handles still have to be be copied + * to the result when there are any unselected curves of the corresponding type. */ + retrieve_attribute_spans(ids_no_interpolation, + src_component, + dst_component, + result.src_no_interpolation, + result.dst_no_interpolation, + result.dst_attributes); + + dst_curves.update_customdata_pointers(); +} + +/** + * Copy the provided point attribute values between all curves in the #curve_ranges index + * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. + */ +template<typename T> +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> curve_ranges, + const Span<T> src, + const MutableSpan<T> dst) +{ + threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { + for (const IndexRange range : curve_ranges.slice(range)) { + const IndexRange src_points = src_curves.points_for_curves(range); + const IndexRange dst_points = dst_curves.points_for_curves(range); + /* The arrays might be large, so a threaded copy might make sense here too. */ + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }); +} +static void copy_between_curves(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const Span<IndexRange> unselected_ranges, + const GSpan src, + const GMutableSpan dst) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); + }); +} + +static Curves *resample_to_uniform(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + /* Create the new curves without any points and evaluate the final count directly + * into the offsets array, in order to be accumulated into offsets later. */ + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(count_field, dst_offsets); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + + VArray<bool> curves_cyclic = src_curves.cyclic(); + VArray<int8_t> curve_types = src_curves.curve_types(); + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + src_curves.ensure_evaluated_lengths(); + + /* Sampling arbitrary attributes works by first interpolating them to the curve's standard + * "evaluated points" and then interpolating that result with the uniform samples. This is + * potentially wasteful when down-sampling a curve to many fewer points. There are two possible + * solutions: only sample the necessary points for interpolation, or first sample curve + * parameter/segment indices and evaluate the curve directly. */ + Array<int> sample_indices(dst_curves.points_num()); + Array<float> sample_factors(dst_curves.points_num()); + + /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on + * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a + * time or one curve at a time. */ + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + Vector<std::byte> evaluated_buffer; + + /* Gather uniform samples based on the accumulated lengths of the original curve. */ + for (const int i_curve : sliced_selection) { + const bool cyclic = curves_cyclic[i_curve]; + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::create_uniform_samples( + src_curves.evaluated_lengths_for_curve(i_curve, cyclic), + curves_cyclic[i_curve], + sample_indices.as_mutable_span().slice(dst_points), + sample_factors.as_mutable_span().slice(dst_points)); + } + + /* For every attribute, evaluate attributes from every curve in the range in the original + * curve's "evaluated points", then use linear interpolation to sample to the result. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + + if (curve_types[i_curve] == CURVE_TYPE_POLY) { + length_parameterize::linear_interpolation(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + else { + const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); + evaluated_buffer.clear(); + evaluated_buffer.resize(sizeof(T) * evaluated_size); + MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); + src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); + + length_parameterize::linear_interpolation(evaluated.as_span(), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + } + }); + } + + /* Interpolate the evaluated positions to the resampled curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst_positions.slice(dst_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +Curves *resample_to_count(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<int> &count_field) +{ + return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field)); +} + +Curves *resample_to_length(const CurveComponent &src_component, + const fn::Field<bool> &selection_field, + const fn::Field<float> &segment_length_field) +{ + return resample_to_uniform( + src_component, selection_field, get_count_input_from_length(segment_length_field)); +} + +Curves *resample_to_evaluated(const CurveComponent &src_component, + const fn::Field<bool> &selection_field) +{ + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_component.get_for_read()->geometry); + + bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( + src_curves.curves_range(), nullptr); + + Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + + /* Directly copy curve attributes, since they stay the same (except for curve types). */ + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + /* All resampled curves are poly curves. */ + dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); + MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); + + src_curves.ensure_evaluated_offsets(); + threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { + for (const int i : selection.slice(range)) { + dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); + } + }); + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_offsets); + bke::curves::accumulate_counts_to_offsets(dst_offsets); + + dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); + + /* Create the correct number of uniform-length samples for every selected curve. */ + Span<float3> evaluated_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + AttributesForInterpolation attributes; + CurveComponent dst_component; + dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + gather_point_attributes_to_interpolate(src_component, dst_component, attributes); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { + const IndexMask sliced_selection = selection.slice(selection_range); + + /* Evaluate generic point attributes directly to the result attributes. */ + for (const int i_attribute : attributes.dst.index_range()) { + attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { + using T = decltype(dummy); + Span<T> src = attributes.src[i_attribute].typed<T>(); + MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); + + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + src_curves.interpolate_to_evaluated( + i_curve, src.slice(src_points), dst.slice(dst_points)); + } + }); + } + + /* Copy the evaluated positions to the selected curves. */ + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + } + + /* Fill the default value for non-interpolating attributes that still must be copied. */ + for (GMutableSpan dst : attributes.dst_no_interpolation) { + for (const int i_curve : sliced_selection) { + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); + } + } + }); + + /* Any attribute data from unselected curve points can be directly copied. */ + for (const int i : attributes.src.index_range()) { + copy_between_curves( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + copy_between_curves(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + /* Copy positions for unselected curves. */ + Span<float3> src_positions = src_curves.positions(); + copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + + for (bke::OutputAttribute &attribute : attributes.dst_attributes) { + attribute.save(); + } + + return dst_curves_id; +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index e25fff0d6b8..ad4b051a6c2 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -59,7 +59,6 @@ typedef struct PHash { struct PChart; struct PEdge; struct PFace; -struct PHandle; struct PVert; /* Simplices */ @@ -171,7 +170,7 @@ typedef struct PChart { } u; uchar flag; - struct PHandle *handle; + ParamHandle *handle; } PChart; enum PChartFlag { @@ -185,7 +184,7 @@ enum PHandleState { PHANDLE_STATE_STRETCH, }; -typedef struct PHandle { +typedef struct ParamHandle { enum PHandleState state; MemArena *arena; MemArena *polyfill_arena; @@ -204,7 +203,7 @@ typedef struct PHandle { RNG *rng; float blend; bool do_aspect; -} PHandle; +} ParamHandle; /* PHash * - special purpose hash that keeps all its elements in a single linked list. @@ -637,7 +636,7 @@ static void p_chart_topological_sanity_check(PChart *chart) /* Loading / Flushing */ -static void p_vert_load_pin_select_uvs(PHandle *handle, PVert *v) +static void p_vert_load_pin_select_uvs(ParamHandle *handle, PVert *v) { PEdge *e; int nedges = 0, npins = 0; @@ -679,7 +678,7 @@ static void p_vert_load_pin_select_uvs(PHandle *handle, PVert *v) } } -static void p_flush_uvs(PHandle *handle, PChart *chart) +static void p_flush_uvs(ParamHandle *handle, PChart *chart) { PEdge *e; @@ -691,7 +690,7 @@ static void p_flush_uvs(PHandle *handle, PChart *chart) } } -static void p_flush_uvs_blend(PHandle *handle, PChart *chart, float blend) +static void p_flush_uvs_blend(ParamHandle *handle, PChart *chart, float blend) { PEdge *e; float invblend = 1.0f - blend; @@ -742,7 +741,7 @@ static void p_face_restore_uvs(PFace *f) /* Construction (use only during construction, relies on u.key being set */ -static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge *e) +static PVert *p_vert_add(ParamHandle *handle, PHashKey key, const float co[3], PEdge *e) { PVert *v = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*v)); copy_v3_v3(v->co, co); @@ -765,7 +764,7 @@ static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge return v; } -static PVert *p_vert_lookup(PHandle *handle, PHashKey key, const float co[3], PEdge *e) +static PVert *p_vert_lookup(ParamHandle *handle, PHashKey key, const float co[3], PEdge *e) { PVert *v = (PVert *)phash_lookup(handle->hash_verts, key); @@ -789,7 +788,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v) return nv; } -static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys) +static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys) { PHashKey key = PHASH_edge(vkeys[0], vkeys[1]); PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key); @@ -808,9 +807,8 @@ static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys) return NULL; } -static int p_face_exists(ParamHandle *phandle, ParamKey *pvkeys, int i1, int i2, int i3) +static int p_face_exists(ParamHandle *handle, ParamKey *pvkeys, int i1, int i2, int i3) { - PHandle *handle = (PHandle *)phandle; PHashKey *vkeys = (PHashKey *)pvkeys; PHashKey key = PHASH_edge(vkeys[i1], vkeys[i2]); PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key); @@ -833,7 +831,7 @@ static int p_face_exists(ParamHandle *phandle, ParamKey *pvkeys, int i1, int i2, return P_FALSE; } -static PChart *p_chart_new(PHandle *handle) +static PChart *p_chart_new(ParamHandle *handle) { PChart *chart = (PChart *)MEM_callocN(sizeof(*chart), "PChart"); chart->handle = handle; @@ -881,7 +879,10 @@ static PBool p_edge_implicit_seam(PEdge *e, PEdge *ep) return P_FALSE; } -static PBool p_edge_has_pair(PHandle *handle, PEdge *e, PBool topology_from_uvs, PEdge **r_pair) +static PBool p_edge_has_pair(ParamHandle *handle, + PEdge *e, + PBool topology_from_uvs, + PEdge **r_pair) { PHashKey key; PEdge *pe; @@ -930,7 +931,7 @@ static PBool p_edge_has_pair(PHandle *handle, PEdge *e, PBool topology_from_uvs, return (*r_pair != NULL); } -static PBool p_edge_connect_pair(PHandle *handle, +static PBool p_edge_connect_pair(ParamHandle *handle, PEdge *e, PBool topology_from_uvs, PEdge ***stack) @@ -954,7 +955,7 @@ static PBool p_edge_connect_pair(PHandle *handle, return (e->pair != NULL); } -static int p_connect_pairs(PHandle *handle, PBool topology_from_uvs) +static int p_connect_pairs(ParamHandle *handle, PBool topology_from_uvs) { PEdge **stackbase = MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces), "Pstackbase"); @@ -1061,7 +1062,7 @@ static void p_split_vert(PChart *chart, PEdge *e) } } -static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts) +static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts) { PChart **charts = MEM_mallocN(sizeof(*charts) * ncharts, "PCharts"), *nchart; PFace *f, *nextf; @@ -1100,7 +1101,7 @@ static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts) return charts; } -static PFace *p_face_add(PHandle *handle) +static PFace *p_face_add(ParamHandle *handle) { PFace *f; PEdge *e1, *e2, *e3; @@ -1132,7 +1133,7 @@ static PFace *p_face_add(PHandle *handle) return f; } -static PFace *p_face_add_construct(PHandle *handle, +static PFace *p_face_add_construct(ParamHandle *handle, ParamKey key, const ParamKey *vkeys, float *co[4], @@ -1219,7 +1220,7 @@ static PFace *p_face_add_fill(PChart *chart, PVert *v1, PVert *v2, PVert *v3) return f; } -static PBool p_quad_split_direction(PHandle *handle, float **co, PHashKey *vkeys) +static PBool p_quad_split_direction(ParamHandle *handle, float **co, PHashKey *vkeys) { /* Slight bias to prefer one edge over the other in case they are equal, so * that in symmetric models we choose the same split direction instead of @@ -3212,7 +3213,7 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf) } } -static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart) +static PBool p_chart_lscm_solve(ParamHandle *handle, PChart *chart) { LinearSolver *context = chart->u.lscm.context; PVert *v, *pin1 = chart->u.lscm.pin1, *pin2 = chart->u.lscm.pin2; @@ -4361,7 +4362,7 @@ static void p_smooth(PChart *chart) ParamHandle *GEO_uv_parametrizer_construct_begin(void) { - PHandle *handle = MEM_callocN(sizeof(*handle), "PHandle"); + ParamHandle *handle = MEM_callocN(sizeof(*handle), "ParamHandle"); handle->construction_chart = p_chart_new(handle); handle->state = PHANDLE_STATE_ALLOCATED; handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena"); @@ -4375,21 +4376,18 @@ ParamHandle *GEO_uv_parametrizer_construct_begin(void) handle->hash_edges = phash_new((PHashLink **)&handle->construction_chart->edges, 1); handle->hash_faces = phash_new((PHashLink **)&handle->construction_chart->faces, 1); - return (ParamHandle *)handle; + return handle; } -void GEO_uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspx, float aspy) +void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float aspy) { - PHandle *phandle = (PHandle *)handle; - phandle->aspx = aspx; phandle->aspy = aspy; phandle->do_aspect = true; } -void GEO_uv_parametrizer_delete(ParamHandle *handle) +void GEO_uv_parametrizer_delete(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED)); @@ -4426,9 +4424,8 @@ static void p_add_ngon(ParamHandle *handle, ParamBool *select) { /* Allocate memory for polyfill. */ - PHandle *phandle = (PHandle *)handle; - MemArena *arena = phandle->polyfill_arena; - Heap *heap = phandle->polyfill_heap; + MemArena *arena = handle->polyfill_arena; + Heap *heap = handle->polyfill_heap; uint nfilltri = nverts - 2; uint(*tris)[3] = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri); float(*projverts)[2] = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts); @@ -4478,7 +4475,7 @@ static void p_add_ngon(ParamHandle *handle, BLI_memarena_clear(arena); } -void GEO_uv_parametrizer_face_add(ParamHandle *handle, +void GEO_uv_parametrizer_face_add(ParamHandle *phandle, ParamKey key, int nverts, ParamKey *vkeys, @@ -4487,15 +4484,13 @@ void GEO_uv_parametrizer_face_add(ParamHandle *handle, ParamBool *pin, ParamBool *select) { - PHandle *phandle = (PHandle *)handle; - param_assert(phash_lookup(phandle->hash_faces, key) == NULL); param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); param_assert(ELEM(nverts, 3, 4)); if (nverts > 4) { /* ngon */ - p_add_ngon(handle, key, nverts, vkeys, co, uv, pin, select); + p_add_ngon(phandle, key, nverts, vkeys, co, uv, pin, select); } else if (nverts == 4) { /* quad */ @@ -4514,9 +4509,8 @@ void GEO_uv_parametrizer_face_add(ParamHandle *handle, } } -void GEO_uv_parametrizer_edge_set_seam(ParamHandle *handle, ParamKey *vkeys) +void GEO_uv_parametrizer_edge_set_seam(ParamHandle *phandle, ParamKey *vkeys) { - PHandle *phandle = (PHandle *)handle; PEdge *e; param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); @@ -4527,12 +4521,11 @@ void GEO_uv_parametrizer_edge_set_seam(ParamHandle *handle, ParamKey *vkeys) } } -void GEO_uv_parametrizer_construct_end(ParamHandle *handle, +void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, ParamBool fill, ParamBool topology_from_uvs, int *count_fail) { - PHandle *phandle = (PHandle *)handle; PChart *chart = phandle->construction_chart; int i, j, nboundaries = 0; PEdge *outer; @@ -4572,7 +4565,7 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *handle, } for (v = chart->verts; v; v = v->nextlink) { - p_vert_load_pin_select_uvs(handle, v); + p_vert_load_pin_select_uvs(phandle, v); } } @@ -4581,9 +4574,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *handle, phandle->state = PHANDLE_STATE_CONSTRUCTED; } -void GEO_uv_parametrizer_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf) +void GEO_uv_parametrizer_lscm_begin(ParamHandle *phandle, ParamBool live, ParamBool abf) { - PHandle *phandle = (PHandle *)handle; PFace *f; int i; @@ -4598,9 +4590,8 @@ void GEO_uv_parametrizer_lscm_begin(ParamHandle *handle, ParamBool live, ParamBo } } -void GEO_uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed) +void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, int *count_failed) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4638,9 +4629,8 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int } } -void GEO_uv_parametrizer_lscm_end(ParamHandle *handle) +void GEO_uv_parametrizer_lscm_end(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(phandle->state == PHANDLE_STATE_LSCM); @@ -4655,9 +4645,8 @@ void GEO_uv_parametrizer_lscm_end(ParamHandle *handle) phandle->state = PHANDLE_STATE_CONSTRUCTED; } -void GEO_uv_parametrizer_stretch_begin(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_begin(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; PVert *v; PFace *f; @@ -4685,17 +4674,14 @@ void GEO_uv_parametrizer_stretch_begin(ParamHandle *handle) } } -void GEO_uv_parametrizer_stretch_blend(ParamHandle *handle, float blend) +void GEO_uv_parametrizer_stretch_blend(ParamHandle *phandle, float blend) { - PHandle *phandle = (PHandle *)handle; - param_assert(phandle->state == PHANDLE_STATE_STRETCH); phandle->blend = blend; } -void GEO_uv_parametrizer_stretch_iter(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_iter(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4707,10 +4693,8 @@ void GEO_uv_parametrizer_stretch_iter(ParamHandle *handle) } } -void GEO_uv_parametrizer_stretch_end(ParamHandle *handle) +void GEO_uv_parametrizer_stretch_end(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; - param_assert(phandle->state == PHANDLE_STATE_STRETCH); phandle->state = PHANDLE_STATE_CONSTRUCTED; @@ -4718,9 +4702,8 @@ void GEO_uv_parametrizer_stretch_end(ParamHandle *handle) phandle->rng = NULL; } -void GEO_uv_parametrizer_smooth_area(ParamHandle *handle) +void GEO_uv_parametrizer_smooth_area(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; int i; param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); @@ -4738,13 +4721,11 @@ void GEO_uv_parametrizer_smooth_area(ParamHandle *handle) } /* don't pack, just rotate (used for better packing) */ -static void GEO_uv_parametrizer_pack_rotate(ParamHandle *handle, bool ignore_pinned) +static void GEO_uv_parametrizer_pack_rotate(ParamHandle *phandle, bool ignore_pinned) { PChart *chart; int i; - PHandle *phandle = (PHandle *)handle; - for (i = 0; i < phandle->ncharts; i++) { chart = phandle->charts[i]; @@ -4770,9 +4751,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, float trans[2]; double area = 0.0; - PHandle *phandle = (PHandle *)handle; - - if (phandle->ncharts == 0) { + if (handle->ncharts == 0) { return; } @@ -4781,15 +4760,15 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, GEO_uv_parametrizer_pack_rotate(handle, ignore_pinned); } - if (phandle->aspx != phandle->aspy) { - GEO_uv_parametrizer_scale(handle, 1.0f / phandle->aspx, 1.0f / phandle->aspy); + if (handle->aspx != handle->aspy) { + GEO_uv_parametrizer_scale(handle, 1.0f / handle->aspx, 1.0f / handle->aspy); } /* we may not use all these boxes */ - boxarray = MEM_mallocN(phandle->ncharts * sizeof(BoxPack), "BoxPack box"); + boxarray = MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box"); - for (i = 0; i < phandle->ncharts; i++) { - chart = phandle->charts[i]; + for (i = 0; i < handle->ncharts; i++) { + chart = handle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { unpacked++; @@ -4821,8 +4800,8 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, * 0.0 to 1.0 but not give a massive margin */ margin = (margin * (float)area) * 0.1f; unpacked = 0; - for (i = 0; i < phandle->ncharts; i++) { - chart = phandle->charts[i]; + for (i = 0; i < handle->ncharts; i++) { + chart = handle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { unpacked++; @@ -4838,7 +4817,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, } } - BLI_box_pack_2d(boxarray, phandle->ncharts - unpacked, &tot_width, &tot_height); + BLI_box_pack_2d(boxarray, handle->ncharts - unpacked, &tot_width, &tot_height); if (tot_height > tot_width) { scale = tot_height != 0.0f ? (1.0f / tot_height) : 1.0f; @@ -4847,30 +4826,29 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, scale = tot_width != 0.0f ? (1.0f / tot_width) : 1.0f; } - for (i = 0; i < phandle->ncharts - unpacked; i++) { + for (i = 0; i < handle->ncharts - unpacked; i++) { box = boxarray + i; trans[0] = box->x; trans[1] = box->y; - chart = phandle->charts[box->index]; + chart = handle->charts[box->index]; p_chart_uv_translate(chart, trans); p_chart_uv_scale(chart, scale); } MEM_freeN(boxarray); - if (phandle->aspx != phandle->aspy) { - GEO_uv_parametrizer_scale(handle, phandle->aspx, phandle->aspy); + if (handle->aspx != handle->aspy) { + GEO_uv_parametrizer_scale(handle, handle->aspx, handle->aspy); } } -void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned) +void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) { PChart *chart; int i; float tot_uvarea = 0.0f, tot_facearea = 0.0f; float tot_fac, fac; float minv[2], maxv[2], trans[2]; - PHandle *phandle = (PHandle *)handle; if (phandle->ncharts == 0) { return; @@ -4930,9 +4908,8 @@ void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned) } } -void GEO_uv_parametrizer_scale(ParamHandle *handle, float x, float y) +void GEO_uv_parametrizer_scale(ParamHandle *phandle, float x, float y) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4942,9 +4919,8 @@ void GEO_uv_parametrizer_scale(ParamHandle *handle, float x, float y) } } -void GEO_uv_parametrizer_flush(ParamHandle *handle) +void GEO_uv_parametrizer_flush(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; int i; @@ -4964,9 +4940,8 @@ void GEO_uv_parametrizer_flush(ParamHandle *handle) } } -void GEO_uv_parametrizer_flush_restore(ParamHandle *handle) +void GEO_uv_parametrizer_flush_restore(ParamHandle *phandle) { - PHandle *phandle = (PHandle *)handle; PChart *chart; PFace *f; int i; diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 6108629183c..69fc26c99e9 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -65,14 +65,30 @@ set(SRC # Lineart code intern/lineart/lineart_chain.c + intern/lineart/lineart_cpp_bridge.cc intern/lineart/lineart_cpu.c intern/lineart/lineart_ops.c intern/lineart/lineart_util.c intern/lineart/MOD_lineart.h intern/lineart/lineart_intern.h +) + +if(WITH_TBB) +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() +list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} +) +list(APPEND LIB + ${TBB_LIBRARIES} ) +endif() set(LIB ) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index e57b9df03f5..f023b00480c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -12,8 +12,6 @@ #include "BLI_math.h" #include "BLI_string.h" -#include "BLT_translation.h" - #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 1058f861be3..a07ef2eb195 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -218,9 +218,18 @@ static void add_this_collection(Collection *c, if (!c) { return; } + bool default_add = true; + /* Do not do nested collection usage check, this is consistent with lineart calculation, because + * collection usage doesn't have a INHERIT mode. This might initially be derived from the fact + * that an object can be inside multiple collections, but might be irrelevant now with the way + * objects are iterated. Keep this logic for now. */ + if (c->lineart_usage & COLLECTION_LRT_EXCLUDE) { + default_add = false; + } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) { if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { - if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) { + if ((ob->lineart.usage == OBJECT_LRT_INHERIT && default_add) || + 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"); } @@ -239,15 +248,11 @@ static void updateDepsgraph(GpencilModifierData *md, DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - if (lmd->source_type == LRT_SOURCE_OBJECT && lmd->source_object) { - DEG_add_object_relation( - ctx->node, lmd->source_object, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); - DEG_add_object_relation( - ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); - } - else { - add_this_collection(ctx->scene->master_collection, ctx, mode); - } + + /* Always add whole master collection because line art will need the whole scene for + * visibility computation. Line art exclusion is handled inside #add_this_collection. */ + add_this_collection(ctx->scene->master_collection, ctx, mode); + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); @@ -387,7 +392,6 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index fcf1e28c6da..259e62a249c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -122,6 +122,8 @@ static void deformStroke(GpencilModifierData *md, const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); const bool invert_group = (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0; const bool use_curve = (mmd->flag & GP_NOISE_CUSTOM_CURVE) != 0 && mmd->curve_intensity; + const int cfra = (int)DEG_get_ctime(depsgraph); + const bool is_keyframe = (mmd->noise_mode == GP_NOISE_RANDOM_KEYFRAME); if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -148,7 +150,13 @@ static void deformStroke(GpencilModifierData *md, seed += BLI_hash_string(md->name); if (mmd->flag & GP_NOISE_USE_RANDOM) { - seed += ((int)DEG_get_ctime(depsgraph)) / mmd->step; + if (!is_keyframe) { + seed += cfra / mmd->step; + } + else { + /* If change every keyframe, use the last keyframe. */ + seed += gpf->framenum; + } } /* Sanitize as it can create out of bound reads. */ @@ -302,7 +310,12 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random")); - uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "random_mode", 0, NULL, ICON_NONE); + + const int mode = RNA_enum_get(ptr, "random_mode"); + if (mode != GP_NOISE_RANDOM_KEYFRAME) { + uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); + } } static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c index c576cfbe525..414231fcae5 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c @@ -122,6 +122,30 @@ static int remapTime(struct GpencilModifierData *md, nfra = (efra + 1 - (cfra + offset - 1) % (efra - sfra + 1)) - 1; } } + + if (mmd->mode == GP_TIME_MODE_PINGPONG) { + if ((mmd->flag & GP_TIME_KEEP_LOOP) == 0) { + if (((int)(cfra + offset - 1) / (efra - sfra)) % (2)) { + nfra = efra - (cfra + offset - 1) % (efra - sfra); + } + else { + nfra = sfra + (cfra + offset - 1) % (efra - sfra); + } + if (cfra > (efra - sfra) * 2) { + nfra = sfra + offset; + } + } + else { + + if (((int)(cfra + offset - 1) / (efra - sfra)) % (2)) { + nfra = efra - (cfra + offset - 1) % (efra - sfra); + } + else { + nfra = sfra + (cfra + offset - 1) % (efra - sfra); + } + } + } + return nfra; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 5d952991cf7..ad3e1b5d7f2 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -122,17 +122,13 @@ typedef struct LineartEdge { /** We only need link node kind of list here. */ struct LineartEdge *next; struct LineartVert *v1, *v2; - /** - * Local vertex index for two ends, not pouting in #RenderVert because all verts are loaded, so - * as long as fewer than half of the mesh edges are becoming a feature line, we save more memory. - */ - int v1_obindex, v2_obindex; + struct LineartTriangle *t1, *t2; ListBase segments; char min_occ; /** Also for line type determination on chaining. */ - unsigned char flags; + uint16_t flags; unsigned char intersection_mask; /** @@ -158,6 +154,8 @@ typedef struct LineartEdgeChain { /** Chain now only contains one type of segments */ int type; + /** Will only connect chains that has the same loop id. */ + int loop_id; unsigned char material_mask_bits; unsigned char intersection_mask; @@ -171,7 +169,7 @@ typedef struct LineartEdgeChainItem { /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; - unsigned char line_type; + uint16_t line_type; char occlusion; unsigned char material_mask_bits; unsigned char intersection_mask; @@ -189,6 +187,12 @@ typedef struct LineartChainRegisterEntry { char is_left; } LineartChainRegisterEntry; +typedef struct LineartAdjacentEdge { + unsigned int v1; + unsigned int v2; + unsigned int e; +} LineartAdjacentEdge; + enum eLineArtTileRecursiveLimit { /* If tile gets this small, it's already much smaller than a pixel. No need to continue * splitting. */ @@ -200,6 +204,12 @@ enum eLineArtTileRecursiveLimit { #define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100 #define LRT_TILE_EDGE_COUNT_INITIAL 32 +typedef struct LineartPendingEdges { + LineartEdge **array; + int max; + int next; +} LineartPendingEdges; + typedef struct LineartRenderBuffer { struct LineartRenderBuffer *prev, *next; @@ -244,15 +254,9 @@ typedef struct LineartRenderBuffer { int triangle_size; - /* Although using ListBase here, LineartEdge is single linked list. - * list.last is used to store worker progress along the list. - * See lineart_main_occlusion_begin() for more info. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */ + struct LineartPendingEdges pending_edges; + int scheduled_count; ListBase chains; @@ -358,14 +362,11 @@ typedef struct LineartRenderTaskInfo { int thread_id; - /* These lists only denote the part of the main edge list that the thread should iterate over. - * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /** + * #pending_edges here only stores a reference to a portion in + * LineartRenderbuffer::pending_edges, assigned by the occlusion scheduler. + */ + struct LineartPendingEdges pending_edges; } LineartRenderTaskInfo; @@ -383,20 +384,14 @@ typedef struct LineartObjectInfo { bool free_use_mesh; - /* Threads will add lines inside here, when all threads are done, we combine those into the - * ones in LineartRenderBuffer. */ - ListBase contour; - ListBase intersection; - ListBase crease; - ListBase material; - ListBase edge_mark; - ListBase floating; + /* Note: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */ + struct LineartPendingEdges pending_edges; } LineartObjectInfo; typedef struct LineartObjectLoadTaskInfo { struct LineartRenderBuffer *rb; - struct Depsgraph *dg; + int thread_id; /* LinkNode styled list */ LineartObjectInfo *pending; /* Used to spread the load across several threads. This can not overflow. */ @@ -481,7 +476,7 @@ typedef struct LineartBoundingArea { * r_aligned: True when 1) a and b is exactly on the same straight line and 2) a and b share a * common end-point. * - * Important: if r_aligned is true, r_ratio will be either 0 or 1 depending on which point from + * IMPORTANT: if r_aligned is true, r_ratio will be either 0 or 1 depending on which point from * segment a is shared with segment b. If it's a1 then r_ratio is 0, else then r_ratio is 1. This * extra information is needed for line art occlusion stage to work correctly in such cases. */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index b666eb677eb..226b8f532b5 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -219,7 +219,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, es->occlusion, es->material_mask_bits, - e->v1_obindex); + e->v1->index); while (ba && (new_e = lineart_line_get_connected( ba, new_vt, &new_vt, e->flags, e->intersection_mask))) { new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; @@ -256,7 +256,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, es->occlusion, es->material_mask_bits, - new_e->v1_obindex); + new_e->v1->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -282,7 +282,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -295,7 +295,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); } ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } @@ -336,7 +336,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, es->occlusion, es->material_mask_bits, - e->v1_obindex); + e->v1->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -349,7 +349,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) e->flags, last_occlusion, last_transparency, - e->v2_obindex); + e->v2->index); /* Step 3: grow right. */ ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]); @@ -402,7 +402,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v1_obindex); + new_e->v1->index); } } else if (new_vt == new_e->v2) { @@ -428,7 +428,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, es->occlusion, es->material_mask_bits, - new_e->v2_obindex); + new_e->v2->index); last_occlusion = es->occlusion; last_transparency = es->material_mask_bits; } @@ -441,7 +441,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) new_e->flags, last_occlusion, last_transparency, - new_e->v2_obindex); + new_e->v2->index); } ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]); } @@ -617,9 +617,14 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) rb->chains.last = rb->chains.first = NULL; + int loop_id = 0; while ((ec = BLI_pophead(&swap)) != NULL) { ec->next = ec->prev = NULL; BLI_addtail(&rb->chains, ec); + + ec->loop_id = loop_id; + loop_id++; + LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; int fixed_occ = first_eci->occlusion; unsigned char fixed_mask = first_eci->material_mask_bits; @@ -651,6 +656,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb) new_ec = lineart_chain_create(rb); new_ec->chain.first = eci; new_ec->chain.last = ec->chain.last; + new_ec->loop_id = loop_id; ec->chain.last = eci->prev; ((LineartEdgeChainItem *)ec->chain.last)->next = 0; eci->prev = 0; @@ -743,6 +749,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf int occlusion, unsigned char material_mask_bits, unsigned char isec_mask, + int loop_id, float dist, float *result_new_len, LineartBoundingArea *caller_ba) @@ -791,7 +798,11 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf float new_len = rb->use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) : len_v2v2(cre->eci->pos, eci->pos); - if (new_len < dist) { + /* Even if the vertex is not from the same contour loop, we try to chain it still if the + * distance is small enough. This way we can better chain smaller loops and smooth them out + * later. */ + if (((cre->ec->loop_id == loop_id) && (new_len < dist)) || + ((cre->ec->loop_id != loop_id) && (new_len < dist / 10))) { closest_cre = cre; dist = new_len; if (result_new_len) { @@ -815,6 +826,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf occlusion, \ material_mask_bits, \ isec_mask, \ + loop_id, \ dist, \ &adjacent_new_len, \ ba); \ @@ -844,7 +856,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre; float dist = rb->chaining_image_threshold; float dist_l, dist_r; - int occlusion, reverse_main; + int occlusion, reverse_main, loop_id; unsigned char material_mask_bits, isec_mask; ListBase swap = {0}; @@ -863,6 +875,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) continue; } BLI_addtail(&rb->chains, ec); + loop_id = ec->loop_id; if (ec->type == LRT_EDGE_FLAG_LOOSE && (!rb->use_loose_edge_chain)) { continue; @@ -876,10 +889,28 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb) eci_r = ec->chain.last; while ((ba_l = lineart_bounding_area_get_end_point(rb, eci_l)) && (ba_r = lineart_bounding_area_get_end_point(rb, eci_r))) { - closest_cre_l = lineart_chain_get_closest_cre( - rb, ba_l, ec, eci_l, occlusion, material_mask_bits, isec_mask, dist, &dist_l, NULL); - closest_cre_r = lineart_chain_get_closest_cre( - rb, ba_r, ec, eci_r, occlusion, material_mask_bits, isec_mask, dist, &dist_r, NULL); + closest_cre_l = lineart_chain_get_closest_cre(rb, + ba_l, + ec, + eci_l, + occlusion, + material_mask_bits, + isec_mask, + loop_id, + dist, + &dist_l, + NULL); + closest_cre_r = lineart_chain_get_closest_cre(rb, + ba_r, + ec, + eci_r, + occlusion, + material_mask_bits, + isec_mask, + loop_id, + dist, + &dist_r, + NULL); if (closest_cre_l && closest_cre_r) { if (dist_l < dist_r) { closest_cre = closest_cre_l; @@ -971,27 +1002,67 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { - LineartEdgeChainItem *next_eci; - for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { - next_eci = eci->next; - LineartEdgeChainItem *eci2, *eci3, *eci4; - - /* Not enough point to do simplify. */ - if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { - continue; - } - - /* No need to care for different line types/occlusion and so on, because at this stage they - * are all the same within a chain. */ - - /* If p3 is within the p1-p2 segment of a width of "tolerance". */ - if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { - /* And if p4 is on the extension of p1-p2 , we remove p3. */ - if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { - BLI_remlink(&ec->chain, eci3); - next_eci = eci; + /* Go through the chain two times, once from each direction. */ + for (int times = 0; times < 2; times++) { + for (LineartEdgeChainItem *eci = ec->chain.first, *next_eci = eci->next; eci; + eci = next_eci) { + LineartEdgeChainItem *eci2, *eci3, *eci4; + + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + /* Not enough points to simplify. */ + next_eci = eci->next; + continue; } + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. + * + * We need to simplify a chain from this: + * 1-----------2 + * 3-----------4 + * to this: + * 1-----------2--_ + * `--4 */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance", in other words, p3 is + * approximately on the segment of p1-p2. */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + float vec2[2], vec3[2], v2n[2], ratio, len2; + sub_v2_v2v2(vec2, eci2->pos, eci->pos); + sub_v2_v2v2(vec3, eci3->pos, eci->pos); + normalize_v2_v2(v2n, vec2); + ratio = dot_v2v2(v2n, vec3); + len2 = len_v2(vec2); + /* Because this smoothing applies on geometries of different scales in the same scene, + * some small scale features (e.g. the "tails" on the inner ring of a torus geometry) + * could be completely erased if the tolerance value is set for accommodating the entire + * scene. Those situations typically result in (ratio << 0), looks like this: + * 1---2 + * 3-------------------------------4 + * (this sort of long zigzag obviously are "features" that can't be erased) + * setting a ratio of -10 turned out to be a reasonable threshold in tests. */ + if (ratio < len2 && ratio > -len2 * 10) { + /* We only remove p3 if p4 is on the extension of p1->p2. */ + if ((eci4 = eci3->next) && + (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&ec->chain, eci3); + next_eci = eci; + continue; + } + if (!eci4) { + /* See if the last segment's direction is reversed, if so remove that. + * Basically we don't need to preserve p3 if the entire chain looked like this: + * ...----1----3===2 */ + if (len_v2(vec2) > len_v2(vec3)) { + BLI_remlink(&ec->chain, eci3); + } + next_eci = NULL; + continue; + } + } + } + next_eci = eci->next; } + BLI_listbase_reverse(&ec->chain); } } } @@ -1152,6 +1223,8 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol new_ec->object_ref = ec->object_ref; new_ec->type = ec->type; new_ec->level = ec->level; + new_ec->loop_id = ec->loop_id; + new_ec->intersection_mask = ec->intersection_mask; new_ec->material_mask_bits = ec->material_mask_bits; ec = new_ec; } diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc new file mode 100644 index 00000000000..5e741ccbd55 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpp_bridge.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_sort.hh" +#include "BLI_vector.hh" +#include "MOD_lineart.h" +#include "lineart_intern.h" + +static bool cmp_adjacent_items(const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) +{ + int a = p1.v1 - p2.v1; + int b = p1.v2 - p2.v2; + /* parallel_sort() requires cmp() to return true when the first element needs to appear before + * the second element in the sorted array, false otherwise (strict weak ordering), see + * https://en.cppreference.com/w/cpp/named_req/Compare. */ + return a < 0 ? true : (a == 0 ? b < 0 : false); +} + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length) +{ + blender::parallel_sort(ai, ai + length, cmp_adjacent_items); +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 24e11f6be3b..016b70cedb0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -8,6 +8,7 @@ #include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" +#include "BLI_edgehash.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -20,6 +21,7 @@ #include "BKE_collection.h" #include "BKE_customdata.h" #include "BKE_deform.h" +#include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_gpencil.h" @@ -28,6 +30,8 @@ #include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_runtime.h" #include "BKE_object.h" #include "BKE_pointcache.h" #include "BKE_scene.h" @@ -93,7 +97,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, double *from, double *to); -static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e); +static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e); static LineartCache *lineart_init_cache(void); @@ -408,38 +412,26 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRenderTaskInfo *rti) { - LineartEdge *data; - int i; int res = 0; + int starting_index; BLI_spin_lock(&rb->lock_task); -#define LRT_ASSIGN_OCCLUSION_TASK(name) \ - if (rb->name.last) { \ - data = rb->name.last; \ - rti->name.first = (void *)data; \ - for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \ - data = data->next; \ - } \ - rti->name.last = data; \ - rb->name.last = data; \ - res = 1; \ - } \ - else { \ - rti->name.first = rti->name.last = NULL; \ - } - - LRT_ASSIGN_OCCLUSION_TASK(contour); - LRT_ASSIGN_OCCLUSION_TASK(intersection); - LRT_ASSIGN_OCCLUSION_TASK(crease); - LRT_ASSIGN_OCCLUSION_TASK(material); - LRT_ASSIGN_OCCLUSION_TASK(edge_mark); - LRT_ASSIGN_OCCLUSION_TASK(floating); - -#undef LRT_ASSIGN_OCCLUSION_TASK + starting_index = rb->scheduled_count; + rb->scheduled_count += LRT_THREAD_EDGE_COUNT; BLI_spin_unlock(&rb->lock_task); + if (starting_index >= rb->pending_edges.next) { + res = 0; + } + else { + rti->pending_edges.array = &rb->pending_edges.array[starting_index]; + int remaining = rb->pending_edges.next - starting_index; + rti->pending_edges.max = MIN2(remaining, LRT_THREAD_EDGE_COUNT); + res = 1; + } + return res; } @@ -449,28 +441,8 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR LineartEdge *eip; while (lineart_occlusion_make_task_info(rb, rti)) { - - for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) { - lineart_occlusion_single_line(rb, eip, rti->thread_id); - } - - for (eip = rti->floating.first; eip && eip != rti->floating.last; eip = eip->next) { + for (int i = 0; i < rti->pending_edges.max; i++) { + eip = rti->pending_edges.array[i]; lineart_occlusion_single_line(rb, eip, rti->thread_id); } } @@ -488,15 +460,6 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) "Task Pool"); int i; - /* The "last" entry is used to store worker progress in the whole list. - * These list themselves are single-direction linked, with list.first being the head. */ - rb->contour.last = rb->contour.first; - rb->crease.last = rb->crease.first; - rb->intersection.last = rb->intersection.first; - rb->material.last = rb->material.first; - rb->edge_mark.last = rb->edge_mark.first; - rb->floating.last = rb->floating.first; - TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); for (i = 0; i < thread_count; i++) { @@ -753,13 +716,12 @@ static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int (tri->v[v2] == e->v1 && tri->v[v1] == e->v2)); } -static void lineart_discard_duplicated_edges(LineartEdge *old_e, int v1id, int v2id) +static void lineart_discard_duplicated_edges(LineartEdge *old_e) { LineartEdge *e = old_e; - e++; - while (e->v1_obindex == v1id && e->v2_obindex == v2id) { - e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + while (e->flags & LRT_EDGE_FLAG_NEXT_IS_DUPLICATION) { e++; + e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; } } @@ -789,8 +751,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, int v_count = *r_v_count; int e_count = *r_e_count; int t_count = *r_t_count; - int v1_obi, v2_obi; - char new_flag = 0; + uint16_t new_flag = 0; LineartEdge *new_e, *e, *old_e; LineartEdgeSegment *es; @@ -813,13 +774,9 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e = new_e; #define INCREASE_EDGE \ - v1_obi = e->v1_obindex; \ - v2_obi = e->v2_obindex; \ new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \ e_count++; \ e = new_e; \ - e->v1_obindex = v1_obi; \ - e->v2_obindex = v2_obi; \ es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ BLI_addtail(&e->segments, es); @@ -828,15 +785,17 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, old_e = ta->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(old_e, old_e->v1_obindex, old_e->v2_obindex); \ + lineart_discard_duplicated_edges(old_e); \ INCREASE_EDGE \ e->v1 = (v1_link); \ e->v2 = (v2_link); \ + e->v1->index = (v1_link)->index; \ + e->v2->index = (v1_link)->index; \ e->flags = new_flag; \ e->object_ref = ob; \ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ - lineart_add_edge_to_list(rb, e); \ + lineart_add_edge_to_array(&rb->pending_edges, e); \ } #define RELINK_EDGE(e_num, new_tri) \ @@ -849,15 +808,15 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, #define REMOVE_TRIANGLE_EDGE \ if (ta->e[0]) { \ ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[0], ta->e[0]->v1_obindex, ta->e[0]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[0]); \ } \ if (ta->e[1]) { \ ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[1], ta->e[1]->v1_obindex, ta->e[1]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[1]); \ } \ if (ta->e[2]) { \ ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[2], ta->e[2]->v1_obindex, ta->e[2]->v2_obindex); \ + lineart_discard_duplicated_edges(ta->e[2]); \ } switch (in0 + in1 + in2) { @@ -922,7 +881,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as * `tri->edge` and `tri->v` has the same sequence. and the winding direction @@ -971,7 +930,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[0]; e->v2 = &vt[1]; @@ -1012,7 +971,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1087,7 +1046,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1139,10 +1098,11 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; @@ -1187,10 +1147,11 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_prepend_edge_direct(&rb->contour.first, e); + lineart_add_edge_to_array(&rb->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; + e->t1 = tri1; e->object_ref = ob; @@ -1426,17 +1387,45 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) } } -/** - * Transform a single vert to it's viewing position. - */ -static void lineart_vert_transform( - BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4]) +typedef struct LineartEdgeNeighbor { + int e; + uint16_t flags; + int v1, v2; +} LineartEdgeNeighbor; + +typedef struct VertData { + MVert *mvert; + LineartVert *v_arr; + double (*model_view)[4]; + double (*model_view_proj)[4]; +} VertData; + +static void lineart_mvert_transform_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { + VertData *vert_task_data = (VertData *)userdata; + MVert *m_v = &vert_task_data->mvert[i]; double co[4]; - LineartVert *vt = &RvBuf[index]; - copy_v3db_v3fl(co, v->co); - mul_v3_m4v3_db(vt->gloc, mv_mat, co); - mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co); + LineartVert *v = &vert_task_data->v_arr[i]; + copy_v3db_v3fl(co, m_v->co); + mul_v3_m4v3_db(v->gloc, vert_task_data->model_view, co); + mul_v4_m4v3_db(v->fbcoord, vert_task_data->model_view_proj, co); + v->index = i; +} + +#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6 + +static int lineart_edge_type_duplication_count(char eflag) +{ + int count = 0; + /* See eLineartEdgeFlag for details. */ + for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { + if (eflag & (1 << i)) { + count++; + } + } + return count; } /** @@ -1452,88 +1441,123 @@ static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb, return (LineartTriangle *)b; } -static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, - BMEdge *e, - LineartTriangle *rt_array, - LineartVert *rv_array, - float crease_threshold, - bool use_auto_smooth, - bool use_freestyle_edge, - bool use_freestyle_face, - BMesh *bm_if_freestyle) +typedef struct EdgeFeatData { + LineartRenderBuffer *rb; + Mesh *me; + const MLoopTri *mlooptri; + LineartTriangle *tri_array; + LineartVert *v_array; + float crease_threshold; + bool use_auto_smooth; + bool use_freestyle_face; + int freestyle_face_index; + bool use_freestyle_edge; + int freestyle_edge_index; + LineartEdgeNeighbor *edge_nabr; +} EdgeFeatData; + +typedef struct EdgeFeatReduceData { + int feat_edges; +} EdgeFeatReduceData; + +static void feat_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) { - BMLoop *ll, *lr = NULL; + EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join; + EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk; + feat_chunk_join->feat_edges += feat_chunk->feat_edges; +} - ll = e->l; - if (ll) { - lr = e->l->radial_next; - } +static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict tls) +{ + EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata; + EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk; + Mesh *me = e_feat_data->me; + LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr; + const MLoopTri *mlooptri = e_feat_data->mlooptri; + + uint16_t edge_flag_result = 0; - if (!ll && !lr) { - return LRT_EDGE_FLAG_LOOSE; + /* Because the edge neighbor array contains loop edge pairs, we only need to process the first + * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */ + if (i < edge_nabr[i].e) { + return; } - FreestyleEdge *fel, *fer; bool face_mark_filtered = false; - uint16_t edge_flag_result = 0; + bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark); bool only_contour = false; - - if (use_freestyle_face && rb->filter_face_mark) { - fel = CustomData_bmesh_get(&bm_if_freestyle->pdata, ll->f->head.data, CD_FREESTYLE_FACE); - if (ll != lr && lr) { - fer = CustomData_bmesh_get(&bm_if_freestyle->pdata, lr->f->head.data, CD_FREESTYLE_FACE); + if (enable_face_mark) { + FreestyleFace *ff1, *ff2; + int index = e_feat_data->freestyle_face_index; + if (index > -1) { + ff1 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[i / 3].poly]; + } + if (edge_nabr[i].e > -1) { + ff2 = &((FreestyleFace *)me->pdata.layers[index].data)[mlooptri[edge_nabr[i].e / 3].poly]; } else { - /* Handles mesh boundary case */ - fer = fel; + /* Handle mesh boundary cases: We want mesh boundaries to respect + * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code + * path is simper when it's assuming both ff1 and ff2 not NULL. */ + ff2 = ff1; } - if (rb->filter_face_mark_boundaries ^ rb->filter_face_mark_invert) { - if ((fel->flag & FREESTYLE_FACE_MARK) || (fer->flag & FREESTYLE_FACE_MARK)) { + if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) { + if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) { face_mark_filtered = true; } } else { - if ((fel->flag & FREESTYLE_FACE_MARK) && (fer->flag & FREESTYLE_FACE_MARK) && (fer != fel)) { + if ((ff1->flag & FREESTYLE_FACE_MARK) && (ff2->flag & FREESTYLE_FACE_MARK) && (ff2 != ff1)) { face_mark_filtered = true; } } - if (rb->filter_face_mark_invert) { + if (e_feat_data->rb->filter_face_mark_invert) { face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { - if (rb->filter_face_mark_keep_contour) { + edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; + if (e_feat_data->rb->filter_face_mark_keep_contour) { only_contour = true; } - else { - return 0; - } } } + if (enable_face_mark && !face_mark_filtered && !only_contour) { + return; + } + /* Mesh boundary */ - if (!lr || ll == lr) { - return (edge_flag_result | LRT_EDGE_FLAG_CONTOUR); + if (edge_nabr[i].e == -1) { + edge_nabr[i].flags = LRT_EDGE_FLAG_CONTOUR; + reduce_data->feat_edges += 1; + return; } LineartTriangle *tri1, *tri2; - LineartVert *l; + LineartVert *vert; + LineartRenderBuffer *rb = e_feat_data->rb; + + int f1 = i / 3, f2 = edge_nabr[i].e / 3; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f)); - tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f)); + tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1); + tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2); - l = &rv_array[BM_elem_index_get(e->v1)]; + vert = &e_feat_data->v_array[edge_nabr[i].v1]; - double vv[3]; - double *view_vector = vv; + double view_vector_persp[3]; + double *view_vector = view_vector_persp; 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 || material_back_face) { - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); + sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc); } else { view_vector = rb->view_vector; @@ -1542,11 +1566,10 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, dot_1 = dot_v3v3_db(view_vector, tri1->gn); dot_2 = dot_v3v3_db(view_vector, tri2->gn); - if (rb->use_contour && (result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - /* Because the ray points towards the camera, so back-face is when dot value being negative. */ if (rb->use_back_face_culling) { if (dot_1 < 0) { tri1->flags |= LRT_CULL_DISCARD; @@ -1564,127 +1587,182 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, } } } - else { - view_vector = rb->view_vector; - } - if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) { - edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; - } + if (!only_contour) { - /* For when face mark filtering decided that we discard the face but keep_contour option is on. - * so we still have correct full contour around the object. */ - if (only_contour) { - return edge_flag_result; - } + if (rb->use_crease) { + bool do_crease = true; + if (!rb->force_crease && !e_feat_data->use_auto_smooth && + (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) && + (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) { + do_crease = false; + } + if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) { + edge_flag_result |= LRT_EDGE_FLAG_CREASE; + } + } - /* 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; + int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; + int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; + + if (rb->use_material && mat1 != mat2) { + edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + } + } + else { /* only_contour */ + if (!edge_flag_result) { /* Other edge types inhibited */ + return; } } - if (rb->use_crease) { - if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { + int real_edges[3]; + BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges); + + if (real_edges[i % 3] >= 0) { + MEdge *medge = &me->medge[real_edges[i % 3]]; + + if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } - else { - bool do_crease = true; - if (!rb->force_crease && !use_auto_smooth && - (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) { - do_crease = false; - } - if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { - edge_flag_result |= LRT_EDGE_FLAG_CREASE; + + if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) { + FreestyleEdge *fe; + int index = e_feat_data->freestyle_edge_index; + fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]]; + if (fe->flag & FREESTYLE_EDGE_MARK) { + edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; } } } - if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) { - edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + + edge_nabr[i].flags = edge_flag_result; + + if (edge_flag_result) { + /* Only allocate for feature edge (instead of all edges) to save memory. + * If allow duplicated edges, one edge gets added multiple times if it has multiple types. + */ + reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ? + lineart_edge_type_duplication_count(edge_flag_result) : + 1; } - if (use_freestyle_edge && rb->use_edge_marks) { - FreestyleEdge *fe; - fe = CustomData_bmesh_get(&bm_if_freestyle->edata, e->head.data, CD_FREESTYLE_EDGE); - if (fe->flag & FREESTYLE_EDGE_MARK) { - edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; - } +} + +typedef struct LooseEdgeData { + int loose_count; + int loose_max; + MEdge **loose_array; + Mesh *me; +} LooseEdgeData; + +static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count) +{ + MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array"); + if (loose_data->loose_array) { + memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max); + MEM_freeN(loose_data->loose_array); } - return edge_flag_result; + loose_data->loose_max = count; + loose_data->loose_array = new_arr; } -static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e) +static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData *to_be_joined) { - switch (e->flags) { - case LRT_EDGE_FLAG_CONTOUR: - lineart_prepend_edge_direct(&rb->contour.first, e); - break; - case LRT_EDGE_FLAG_CREASE: - lineart_prepend_edge_direct(&rb->crease.first, e); - break; - case LRT_EDGE_FLAG_MATERIAL: - lineart_prepend_edge_direct(&rb->material.first, e); - break; - case LRT_EDGE_FLAG_EDGE_MARK: - lineart_prepend_edge_direct(&rb->edge_mark.first, e); - break; - case LRT_EDGE_FLAG_INTERSECTION: - lineart_prepend_edge_direct(&rb->intersection.first, e); - break; - case LRT_EDGE_FLAG_LOOSE: - lineart_prepend_edge_direct(&rb->floating.first, e); - break; + if (!to_be_joined->loose_array) { + return; + } + int new_count = loose_data->loose_count + to_be_joined->loose_count; + if (new_count >= loose_data->loose_max) { + lineart_loose_data_reallocate(loose_data, new_count); + } + memcpy(&loose_data->loose_array[loose_data->loose_count], + to_be_joined->loose_array, + sizeof(MEdge *) * to_be_joined->loose_count); + loose_data->loose_count += to_be_joined->loose_count; + MEM_freeN(to_be_joined->loose_array); +} + +static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e) +{ + if (loose_data->loose_count >= loose_data->loose_max) { + int min_amount = MAX2(100, loose_data->loose_count * 2); + lineart_loose_data_reallocate(loose_data, min_amount); } + loose_data->loose_array[loose_data->loose_count] = e; + loose_data->loose_count++; } -static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e) +static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata), + const int i, + const TaskParallelTLS *__restrict tls) { + LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk; + Mesh *me = loose_data->me; -#define LRT_ASSIGN_EDGE(name) \ - lineart_prepend_edge_direct(&obi->name.first, e); \ - if (!obi->name.last) { \ - obi->name.last = e; \ + if (me->medge[i].flag & ME_LOOSEEDGE) { + lineart_add_loose_edge(loose_data, &me->medge[i]); } - switch (e->flags) { - case LRT_EDGE_FLAG_CONTOUR: - LRT_ASSIGN_EDGE(contour); - break; - case LRT_EDGE_FLAG_CREASE: - LRT_ASSIGN_EDGE(crease); - break; - case LRT_EDGE_FLAG_MATERIAL: - LRT_ASSIGN_EDGE(material); - break; - case LRT_EDGE_FLAG_EDGE_MARK: - LRT_ASSIGN_EDGE(edge_mark); - break; - case LRT_EDGE_FLAG_INTERSECTION: - LRT_ASSIGN_EDGE(intersection); - break; - case LRT_EDGE_FLAG_LOOSE: - LRT_ASSIGN_EDGE(floating); - break; +} + +static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + LooseEdgeData *final = (LooseEdgeData *)chunk_join; + LooseEdgeData *loose_chunk = (LooseEdgeData *)chunk; + lineart_join_loose_edge_arr(final, loose_chunk); +} + +static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) +{ + if (pe->next >= pe->max || !pe->max) { + if (!pe->max) { + pe->max = 1000; + } + + LineartEdge **new_array = MEM_mallocN(sizeof(LineartEdge *) * pe->max * 2, + "LineartPendingEdges array"); + if (LIKELY(pe->array)) { + memcpy(new_array, pe->array, sizeof(LineartEdge *) * pe->max); + MEM_freeN(pe->array); + } + pe->max *= 2; + pe->array = new_array; + } + pe->array[pe->next] = e; + pe->next++; +} + +static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge *e) +{ + lineart_add_edge_to_array(&obi->pending_edges, e); +} + +/* Note: For simplicity, this function doesn't actually do anything if you already have data in + * #pe. */ +static void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count) +{ + if (pe->max || pe->array) { + return; } -#undef LRT_ASSIGN_EDGE + + pe->max = count; + LineartEdge **new_array = MEM_mallocN(sizeof(LineartEdge *) * pe->max, + "LineartPendingEdges array final"); + pe->array = new_array; } -static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi) +static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartObjectInfo *obi) { -#define LRT_OBI_TO_RB(name) \ - if (obi->name.last) { \ - ((LineartEdge *)obi->name.last)->next = rb->name.first; \ - rb->name.first = obi->name.first; \ - } - LRT_OBI_TO_RB(contour); - LRT_OBI_TO_RB(crease); - LRT_OBI_TO_RB(material); - LRT_OBI_TO_RB(edge_mark); - LRT_OBI_TO_RB(intersection); - LRT_OBI_TO_RB(floating); -#undef LRT_OBI_TO_RB + /* In case of line art "occlusion only" or contour not enabled, it's possible for an object to + * not produce any feature lines. */ + if (!obi->pending_edges.array) { + return; + } + memcpy(&pe->array[pe->next], + obi->pending_edges.array, + sizeof(LineartEdge *) * obi->pending_edges.next); + MEM_freeN(obi->pending_edges.array); + pe->next += obi->pending_edges.next; } static void lineart_triangle_adjacent_assign(LineartTriangle *tri, @@ -1702,298 +1780,416 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri, } } -static int lineart_edge_type_duplication_count(char eflag) +typedef struct TriData { + LineartObjectInfo *ob_info; + const MLoopTri *mlooptri; + LineartVert *vert_arr; + LineartTriangle *tri_arr; + int lineart_triangle_size; + LineartTriangleAdjacent *tri_adj; +} TriData; + +static void lineart_load_tri_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - int count = 0; - /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < 6; i++) { - if (eflag & (1 << i)) { - count++; - } - } - return count; + TriData *tri_task_data = (TriData *)userdata; + Mesh *me = tri_task_data->ob_info->original_me; + LineartObjectInfo *ob_info = tri_task_data->ob_info; + const MLoopTri *mlooptri = &tri_task_data->mlooptri[i]; + LineartVert *vert_arr = tri_task_data->vert_arr; + LineartTriangle *tri = tri_task_data->tri_arr; + + tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i); + + int v1 = me->mloop[mlooptri->tri[0]].v; + int v2 = me->mloop[mlooptri->tri[1]].v; + int v3 = me->mloop[mlooptri->tri[2]].v; + + tri->v[0] = &vert_arr[v1]; + tri->v[1] = &vert_arr[v2]; + tri->v[2] = &vert_arr[v3]; + + /* Material mask bits and occlusion effectiveness assignment. */ + Material *mat = BKE_object_material_get(ob_info->original_ob, + me->mpoly[mlooptri->poly].mat_nr + 1); + tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? + 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 = ob_info->override_intersection_mask; + + double gn[3]; + float no[3]; + normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co); + copy_v3db_v3fl(gn, no); + mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn); + normalize_v3_db(tri->gn); + + if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) { + tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; + } + else if (ob_info->usage == OBJECT_LRT_NO_INTERSECTION || + ob_info->usage == OBJECT_LRT_OCCLUSION_ONLY) { + tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; + } + + /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ + tri->intersecting_verts = (void *)&tri_task_data->tri_adj[i]; } -static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) +typedef struct EdgeNeighborData { + LineartEdgeNeighbor *edge_nabr; + LineartAdjacentEdge *adj_e; + MLoopTri *mlooptri; + MLoop *mloop; +} EdgeNeighborData; + +static void lineart_edge_neighbor_init_task(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) { - BMesh *bm; - BMVert *v; - BMFace *f; - BMEdge *e; - BMLoop *loop; - LineartEdge *la_e; - LineartEdgeSegment *la_s; - LineartTriangle *tri; - LineartTriangleAdjacent *orta; - double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view, - (*normal)[4] = obi->normal; - LineartElementLinkNode *eln; - LineartVert *orv; - LineartEdge *o_la_e; - LineartEdgeSegment *o_la_s; - LineartTriangle *ort; - Object *orig_ob; - bool can_find_freestyle_edge = false; - bool can_find_freestyle_face = false; - int i; - float use_crease = 0; + EdgeNeighborData *en_data = (EdgeNeighborData *)userdata; + LineartAdjacentEdge *adj_e = &en_data->adj_e[i]; + MLoopTri *looptri = &en_data->mlooptri[i / 3]; + LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i]; + MLoop *mloop = en_data->mloop; + + adj_e->e = i; + adj_e->v1 = mloop[looptri->tri[i % 3]].v; + adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v; + if (adj_e->v1 > adj_e->v2) { + SWAP(unsigned int, adj_e->v1, adj_e->v2); + } + edge_nabr->e = -1; + + edge_nabr->v1 = adj_e->v1; + edge_nabr->v2 = adj_e->v2; + edge_nabr->flags = 0; +} - int usage = obi->usage; +static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edges) +{ + /* Because the mesh is triangulated, so `me->totedge` should be reliable? */ + LineartAdjacentEdge *adj_e = MEM_mallocN(sizeof(LineartAdjacentEdge) * total_edges, + "LineartAdjacentEdge arr"); + LineartEdgeNeighbor *edge_nabr = MEM_mallocN(sizeof(LineartEdgeNeighbor) * total_edges, + "LineartEdgeNeighbor arr"); - if (obi->original_me->edit_mesh) { - /* Do not use edit_mesh directly because we will modify it, so create a copy. */ - bm = BM_mesh_copy(obi->original_me->edit_mesh->bm); - } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me))); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - obi->original_me, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - .calc_vert_normal = true, - })); - } + MLoopTri *mlooptri = me->runtime.looptris.array; - if (obi->free_use_mesh) { - BKE_id_free(NULL, obi->original_me); - } + TaskParallelSettings en_settings; + BLI_parallel_range_settings_defaults(&en_settings); + /* Set the minimum amount of edges a thread has to process. */ + en_settings.min_iter_per_thread = 50000; + + EdgeNeighborData en_data; + en_data.adj_e = adj_e; + en_data.edge_nabr = edge_nabr; + en_data.mlooptri = mlooptri; + en_data.mloop = me->mloop; + + BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings); - if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm); - BMOperator findop, weldop; + lineart_sort_adjacent_items(adj_e, total_edges); - /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ - BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001); + for (int i = 0; i < total_edges - 1; i++) { + if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) { + edge_nabr[adj_e[i].e].e = adj_e[i + 1].e; + edge_nabr[adj_e[i + 1].e].e = adj_e[i].e; + } + } - BMO_op_exec(bm, &findop); + MEM_freeN(adj_e); - /* Weld the vertices. */ - BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts"); - BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap"); - BMO_op_exec(bm, &weldop); + return edge_nabr; +} - BMO_op_finish(bm, &findop); - BMO_op_finish(bm, &weldop); +static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf) +{ + LineartElementLinkNode *elem_link_node; + LineartVert *la_v_arr; + LineartEdge *la_edge_arr; + LineartEdgeSegment *la_seg_arr; + LineartTriangle *la_tri_arr; - MEM_freeN(em); + Mesh *me = ob_info->original_me; + + if (!me->totedge) { + return; } - BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL); - BM_mesh_normals_update(bm); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + /* Triangulate. */ + const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me); + const int tot_tri = BKE_mesh_runtime_looptri_len(me); - if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) { - can_find_freestyle_edge = 1; + /* Check if we should look for custom data tags like Freestyle edges or faces. */ + bool can_find_freestyle_edge = false; + int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE); + if (layer_index != -1) { + can_find_freestyle_edge = true; } - if (CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) { + + bool can_find_freestyle_face = false; + layer_index = CustomData_get_active_layer_index(&me->pdata, CD_FREESTYLE_FACE); + if (layer_index != -1) { can_find_freestyle_face = true; } /* If we allow duplicated edges, one edge should get added multiple times if is has been * classified as more than one edge type. This is so we can create multiple different line type * chains containing the same edge. */ - orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert); - ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size); + la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartVert) * me->totvert); + la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + tot_tri * re_buf->triangle_size); - orig_ob = obi->original_ob; + Object *orig_ob = ob_info->original_ob; - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers, + &re_buf->render_data_pool, + la_v_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); - eln->element_count = bm->totvert; - eln->object_ref = orig_ob; - obi->v_eln = eln; + elem_link_node->element_count = me->totvert; + elem_link_node->object_ref = orig_ob; + ob_info->v_eln = elem_link_node; bool use_auto_smooth = false; + float crease_angle = 0; if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { - use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); + crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold); } - else if (obi->original_me->flag & ME_AUTOSMOOTH) { - use_crease = cosf(obi->original_me->smoothresh); + else if (ob_info->original_me->flag & ME_AUTOSMOOTH) { + crease_angle = cosf(ob_info->original_me->smoothresh); use_auto_smooth = true; } else { - use_crease = rb->crease_threshold; + crease_angle = re_buf->crease_threshold; } /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates * erroneous detection on creases. Future configuration should allow options. */ if (orig_ob->type == OB_FONT) { - eln->flags |= LRT_ELEMENT_BORDER_ONLY; + elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY; } - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers, + &re_buf->render_data_pool, + la_tri_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); + + int usage = ob_info->usage; - eln->element_count = bm->totface; - eln->object_ref = orig_ob; - eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); + elem_link_node->element_count = tot_tri; + elem_link_node->object_ref = orig_ob; + elem_link_node->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0); /* Note this memory is not from pool, will be deleted after culling. */ - orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent"); + LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri, + "LineartTriangleAdjacent"); /* Link is minimal so we use pool anyway. */ - BLI_spin_lock(&rb->lock_task); + BLI_spin_lock(&re_buf->lock_task); lineart_list_append_pointer_pool_thread( - &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta); - BLI_spin_unlock(&rb->lock_task); - - for (i = 0; i < bm->totvert; i++) { - v = BM_vert_at_index(bm, i); - lineart_vert_transform(v, i, orv, model_view, model_view_proj); - orv[i].index = i; - } - /* Register a global index increment. See #lineart_triangle_share_edge() and - * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually - * overflow, in such large scene it's virtually impossible for two vertex of the same numeric - * index to come close together. */ - obi->global_i_offset = bm->totvert; - - tri = ort; - for (i = 0; i < bm->totface; i++) { - f = BM_face_at_index(bm, i); - - loop = f->l_first; - tri->v[0] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[1] = &orv[BM_elem_index_get(loop->v)]; - loop = loop->next; - tri->v[2] = &orv[BM_elem_index_get(loop->v)]; - - /* Material mask bits and occlusion effectiveness assignment. */ - Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1); - tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? - 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; - - double gn[3]; - copy_v3db_v3fl(gn, f->no); - mul_v3_mat3_m4v3_db(tri->gn, normal, gn); - normalize_v3_db(tri->gn); - - if (usage == OBJECT_LRT_INTERSECTION_ONLY) { - tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; - } - else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { - tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; - } - - /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */ - tri->intersecting_verts = (void *)&orta[i]; - - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); - } - - /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */ - - int allocate_la_e = 0; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); - - /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - uint16_t eflag = lineart_identify_feature_line(rb, - e, - ort, - orv, - use_crease, - use_auto_smooth, - can_find_freestyle_edge, - can_find_freestyle_face, - bm); - if (eflag) { - /* Only allocate for feature lines (instead of all lines) to save memory. - * If allow duplicated edges, one edge gets added multiple times if it has multiple types. - */ - allocate_la_e += rb->allow_duplicated_types ? lineart_edge_type_duplication_count(eflag) : 1; + &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj); + BLI_spin_unlock(&re_buf->lock_task); + + /* Convert all vertices to lineart verts. */ + TaskParallelSettings vert_settings; + BLI_parallel_range_settings_defaults(&vert_settings); + /* Set the minimum amount of verts a thread has to process. */ + vert_settings.min_iter_per_thread = 4000; + + VertData vert_data; + vert_data.mvert = me->mvert; + vert_data.v_arr = la_v_arr; + vert_data.model_view = ob_info->model_view; + vert_data.model_view_proj = ob_info->model_view_proj; + + BLI_task_parallel_range( + 0, me->totvert, &vert_data, lineart_mvert_transform_task, &vert_settings); + + /* Convert all mesh triangles into lineart triangles. + * Also create an edge map to get connectivity between edges and triangles. */ + TaskParallelSettings tri_settings; + BLI_parallel_range_settings_defaults(&tri_settings); + /* Set the minimum amount of triangles a thread has to process. */ + tri_settings.min_iter_per_thread = 4000; + + TriData tri_data; + tri_data.ob_info = ob_info; + tri_data.mlooptri = mlooptri; + tri_data.vert_arr = la_v_arr; + tri_data.tri_arr = la_tri_arr; + tri_data.lineart_triangle_size = re_buf->triangle_size; + tri_data.tri_adj = tri_adj; + + unsigned int total_edges = tot_tri * 3; + + BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings); + + /* Check for contour lines in the mesh. + * IE check if the triangle edges lies in area where the triangles go from front facing to back + * facing. + */ + EdgeFeatReduceData edge_reduce = {0}; + TaskParallelSettings edge_feat_settings; + BLI_parallel_range_settings_defaults(&edge_feat_settings); + /* Set the minimum amount of edges a thread has to process. */ + edge_feat_settings.min_iter_per_thread = 4000; + edge_feat_settings.userdata_chunk = &edge_reduce; + edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData); + edge_feat_settings.func_reduce = feat_data_sum_reduce; + + EdgeFeatData edge_feat_data = {0}; + edge_feat_data.rb = re_buf; + edge_feat_data.me = me; + edge_feat_data.mlooptri = mlooptri; + edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges); + edge_feat_data.tri_array = la_tri_arr; + edge_feat_data.v_array = la_v_arr; + edge_feat_data.crease_threshold = crease_angle; + edge_feat_data.use_auto_smooth = use_auto_smooth; + edge_feat_data.use_freestyle_face = can_find_freestyle_face; + edge_feat_data.use_freestyle_edge = can_find_freestyle_edge; + if (edge_feat_data.use_freestyle_face) { + edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&me->pdata, + CD_FREESTYLE_FACE); + } + if (edge_feat_data.use_freestyle_edge) { + edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&me->edata, + CD_FREESTYLE_EDGE); + } + + BLI_task_parallel_range(0, + total_edges, + &edge_feat_data, + lineart_identify_mlooptri_feature_edges, + &edge_feat_settings); + + LooseEdgeData loose_data = {0}; + if (re_buf->use_loose) { + /* Only identifying floating edges at this point because other edges has been taken care of + * inside #lineart_identify_mlooptri_feature_edges function. */ + TaskParallelSettings edge_loose_settings; + BLI_parallel_range_settings_defaults(&edge_loose_settings); + edge_loose_settings.min_iter_per_thread = 4000; + edge_loose_settings.func_reduce = loose_data_sum_reduce; + edge_loose_settings.userdata_chunk = &loose_data; + edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData); + loose_data.me = me; + BLI_task_parallel_range( + 0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings); + } + + int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; + + la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdge) * allocate_la_e); + la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + sizeof(LineartEdgeSegment) * allocate_la_e); + BLI_spin_lock(&re_buf->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers, + &re_buf->render_data_pool, + la_edge_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&re_buf->lock_task); + elem_link_node->element_count = allocate_la_e; + elem_link_node->object_ref = orig_ob; + + // Start of the edge/seg arr + LineartEdge *la_edge; + LineartEdgeSegment *la_seg; + la_edge = la_edge_arr; + la_seg = la_seg_arr; + + for (int i = 0; i < total_edges; i++) { + LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i]; + + if (i < edge_nabr->e) { + continue; } - /* Here we just use bm's flag for when loading actual lines, then we don't need to call - * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always - * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; - } - - o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); - o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool, - sizeof(LineartEdgeSegment) * allocate_la_e); - BLI_spin_lock(&rb->lock_task); - eln = lineart_list_append_pointer_pool_sized_thread( - &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&rb->lock_task); - eln->element_count = allocate_la_e; - eln->object_ref = orig_ob; - - la_e = o_la_e; - la_s = o_la_s; - for (i = 0; i < bm->totedge; i++) { - e = BM_edge_at_index(bm, i); /* Not a feature line, so we skip. */ - if (!e->head.hflag) { + if (edge_nabr->flags == 0) { continue; } - bool edge_added = false; + LineartEdge *edge_added = NULL; /* See eLineartEdgeFlag for details. */ - for (int flag_bit = 0; flag_bit < 6; flag_bit++) { + for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) { char use_type = 1 << flag_bit; - if (!(use_type & e->head.hflag)) { + if (!(use_type & edge_nabr->flags)) { continue; } - la_e->v1 = &orv[BM_elem_index_get(e->v1)]; - la_e->v2 = &orv[BM_elem_index_get(e->v2)]; - la_e->v1_obindex = la_e->v1->index; - la_e->v2_obindex = la_e->v2->index; - if (e->l) { - int findex = BM_elem_index_get(e->l->f); - la_e->t1 = lineart_triangle_from_index(rb, ort, findex); + la_edge->v1 = &la_v_arr[edge_nabr->v1]; + la_edge->v2 = &la_v_arr[edge_nabr->v2]; + int findex = i / 3; + la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + if (!edge_added) { + lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge); + } + if (edge_nabr->e != -1) { + findex = edge_nabr->e / 3; + la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e); - } - if (e->l->radial_next && e->l->radial_next != e->l) { - findex = BM_elem_index_get(e->l->radial_next->f); - la_e->t2 = lineart_triangle_from_index(rb, ort, findex); - if (!edge_added) { - lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e); - } + lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge); } } - la_e->flags = use_type; - la_e->object_ref = orig_ob; - BLI_addtail(&la_e->segments, la_s); - if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { - lineart_add_edge_to_list_thread(obi, la_e); + la_edge->flags = use_type; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_array_thread(ob_info, la_edge); } - edge_added = true; + if (edge_added) { + edge_added->flags |= LRT_EDGE_FLAG_NEXT_IS_DUPLICATION; + } + + edge_added = la_edge; - la_e++; - la_s++; + la_edge++; + la_seg++; - if (!rb->allow_duplicated_types) { + if (!re_buf->allow_duplicated_types) { break; } } } - /* always free bm as it's a copy from before threading */ - BM_mesh_free(bm); + if (loose_data.loose_array) { + for (int i = 0; i < loose_data.loose_count; i++) { + la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1]; + la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2]; + la_edge->flags = LRT_EDGE_FLAG_LOOSE; + la_edge->object_ref = orig_ob; + BLI_addtail(&la_edge->segments, la_seg); + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || + usage == OBJECT_LRT_NO_INTERSECTION) { + lineart_add_edge_to_array_thread(ob_info, la_edge); + } + la_edge++; + la_seg++; + } + MEM_freeN(loose_data.loose_array); + } + + MEM_freeN(edge_feat_data.edge_nabr); + + if (ob_info->free_use_mesh) { + BKE_id_free(NULL, me); + } } static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), @@ -2001,6 +2197,9 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), { for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { lineart_geometry_object_load(obi, olti->rb); + if (G.debug_value == 4000) { + printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly); + } } } @@ -2085,6 +2284,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ use_olti = &olti_list[i]; } } + use_olti->total_faces += this_face_count; obi->next = use_olti->pending; use_olti->pending = obi; @@ -2093,18 +2293,21 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], double shift_x, double shift_y, - Object *use_ob) + Mesh *use_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(use_ob); - if (!bb) { - /* For lights and empty stuff there will be no bbox. */ + if (!use_mesh) { return false; } + float mesh_min[3], mesh_max[3]; + INIT_MINMAX(mesh_min, mesh_max); + BKE_mesh_minmax(use_mesh, mesh_min, mesh_max); + BoundBox bb = {0}; + BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max); double co[8][4]; double tmp[3]; for (int i = 0; i < 8; i++) { - copy_v3db_v3fl(co[i], bb->vec[i]); + copy_v3db_v3fl(co[i], bb.vec[i]); copy_v3_v3_db(tmp, co[i]); mul_v4_m4v3_db(co[i], model_view_proj, tmp); co[i][0] -= shift_x * 2 * co[i][3]; @@ -2130,6 +2333,69 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], return true; } +static void lineart_object_load_single_instance(LineartRenderBuffer *rb, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + Object *ref_ob, + float use_mat[4][4], + bool is_render, + LineartObjectLoadTaskInfo *olti, + int thread_count) +{ + LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); + obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob); + Mesh *use_mesh; + + if (obi->usage == OBJECT_LRT_EXCLUDE) { + return; + } + + /* Prepare the matrix used for transforming this specific object (instance). This has to be + * done before mesh boundbox check because the function needs that. */ + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, use_mat); + + if (!ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { + return; + } + if (ob->type == OB_MESH) { + use_mesh = BKE_object_get_evaluated_mesh(ob); + if (use_mesh->edit_mesh) { + /* If the object is being edited, then the mesh is not evaluated fully into the final + * result, do not load them. */ + return; + } + } + else { + use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true); + } + + /* In case we still can not get any mesh geometry data from the object */ + if (!use_mesh) { + return; + } + + if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + return; + } + + if (ob->type != OB_MESH) { + obi->free_use_mesh = true; + } + + /* Make normal matrix. */ + float imat[4][4]; + invert_m4_m4(imat, use_mat); + transpose_m4(imat); + copy_m4d_m4(obi->normal, imat); + + obi->original_me = use_mesh; + obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : (Object *)ref_ob); + lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); +} + static void lineart_main_load_geometries( Depsgraph *depsgraph, Scene *scene, @@ -2178,14 +2444,6 @@ static void lineart_main_load_geometries( BLI_listbase_clear(&rb->triangle_buffer_pointers); BLI_listbase_clear(&rb->vertex_buffer_pointers); - int flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | - DEG_ITER_OBJECT_FLAG_VISIBLE; - - /* Instance duplicated & particles. */ - if (allow_duplicates) { - flags |= DEG_ITER_OBJECT_FLAG_DUPLI; - } - int thread_count = rb->thread_count; /* This memory is in render buffer memory pool. so we don't need to free those after loading. @@ -2193,75 +2451,43 @@ static void lineart_main_load_geometries( LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); - bool is_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; + eEvaluationMode eval_mode = DEG_get_mode(depsgraph); + bool is_render = eval_mode == DAG_EVAL_RENDER; - DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) { - LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); - obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); - obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, - ob); - Mesh *use_mesh; + FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { + Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob); - if (obi->usage == OBJECT_LRT_EXCLUDE) { + if (!eval_ob) { continue; } - Object *use_ob = DEG_get_evaluated_object(depsgraph, ob); - /* Prepare the matrix used for transforming this specific object (instance). This has to be - * done before mesh boundbox check because the function needs that. */ - 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_CURVES_LEGACY, OB_SURF, OB_FONT)) { - continue; - } - - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) { - if (G.debug_value == 4000) { - bound_box_discard_count++; - } - continue; - } - - if (use_ob->type == OB_MESH) { - use_mesh = BKE_object_get_evaluated_mesh(use_ob); + if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) { + lineart_object_load_single_instance( + rb, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count); } - else { - /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh - * object in the list. To avoid adding duplicate geometry, ignore evaluated curve objects in - * those cases. */ - if (allow_duplicates && BKE_object_get_evaluated_mesh(ob) != NULL) { - continue; + if (allow_duplicates) { + ListBase *dupli = object_duplilist(depsgraph, scene, eval_ob); + LISTBASE_FOREACH (DupliObject *, dob, dupli) { + if (BKE_object_visibility(eval_ob, eval_mode) & + (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { + Object *ob_ref = (dob->type & OB_DUPLIPARTS) ? eval_ob : dob->ob; + lineart_object_load_single_instance( + rb, depsgraph, scene, dob->ob, ob_ref, dob->mat, is_render, olti, thread_count); + } } - use_mesh = BKE_mesh_new_from_object(depsgraph, use_ob, true, true); - } - - /* In case we still can not get any mesh geometry data from the object */ - if (!use_mesh) { - continue; - } - - if (ob->type != OB_MESH) { - obi->free_use_mesh = true; + free_object_duplilist(dupli); } - - /* Make normal matrix. */ - float imat[4][4]; - invert_m4_m4(imat, ob->obmat); - transpose_m4(imat); - copy_m4d_m4(obi->normal, imat); - - obi->original_me = use_mesh; - obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob); - lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); } - DEG_OBJECT_ITER_END; + FOREACH_SCENE_OBJECT_END; TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + if (G.debug_value == 4000) { + printf("thread count: %d\n", thread_count); + } for (int i = 0; i < thread_count; i++) { olti[i].rb = rb; - olti[i].dg = depsgraph; + olti[i].thread_id = i; BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -2271,6 +2497,17 @@ static void lineart_main_load_geometries( * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */ int global_i = 0; + int edge_count = 0; + for (int i = 0; i < thread_count; i++) { + for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { + if (!obi->v_eln) { + continue; + } + edge_count += obi->pending_edges.next; + } + } + lineart_finalize_object_edge_array_reserve(&rb->pending_edges, edge_count); + for (int i = 0; i < thread_count; i++) { for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { if (!obi->v_eln) { @@ -2281,8 +2518,13 @@ static void lineart_main_load_geometries( for (int vi = 0; vi < v_count; vi++) { v[vi].index += global_i; } + /* Register a global index increment. See #lineart_triangle_share_edge() and + * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might + * eventually overflow, in such large scene it's virtually impossible for two vertex of the + * same numeric index to come close together. */ + obi->global_i_offset = global_i; global_i += v_count; - lineart_finalize_object_edge_list(rb, obi); + lineart_finalize_object_edge_array(&rb->pending_edges, obi); } } @@ -2987,7 +3229,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, result->flags = LRT_EDGE_FLAG_INTERSECTION; result->intersection_mask = (tri->intersection_mask | testing->intersection_mask); - lineart_prepend_edge_direct(&rb->intersection.first, result); + lineart_add_edge_to_array(&rb->pending_edges, result); return result; } @@ -3003,7 +3245,7 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; - /* If this is not the smallest subdivision bounding area. */ + /* If this is not the smallest subdiv bounding area. */ if (ba->child) { lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); @@ -3075,13 +3317,6 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) return; } - memset(&rb->contour, 0, sizeof(ListBase)); - memset(&rb->crease, 0, sizeof(ListBase)); - memset(&rb->intersection, 0, sizeof(ListBase)); - memset(&rb->edge_mark, 0, sizeof(ListBase)); - memset(&rb->material, 0, sizeof(ListBase)); - memset(&rb->floating, 0, sizeof(ListBase)); - BLI_listbase_clear(&rb->chains); BLI_listbase_clear(&rb->wasted_cuts); @@ -3093,6 +3328,10 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb) BLI_spin_end(&rb->lock_cuts); BLI_spin_end(&rb->render_data_pool.lock_mem); + if (rb->pending_edges.array) { + MEM_freeN(rb->pending_edges.array); + } + lineart_mem_destroy(&rb->render_data_pool); } @@ -3187,7 +3426,6 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; - rb->remove_doubles = (lmd->calculation_flags & LRT_REMOVE_DOUBLES) != 0; rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 508061d5d98..1a197c8b4b7 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -67,49 +67,11 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb); void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb); #define LRT_ITER_ALL_LINES_BEGIN \ - LineartEdge *e, *next_e; \ - void **current_head; \ - e = rb->contour.first; \ - if (!e) { \ - e = rb->crease.first; \ - } \ - if (!e) { \ - e = rb->material.first; \ - } \ - if (!e) { \ - e = rb->edge_mark.first; \ - } \ - if (!e) { \ - e = rb->intersection.first; \ - } \ - if (!e) { \ - e = rb->floating.first; \ - } \ - for (current_head = &rb->contour.first; e; e = next_e) { \ - next_e = e->next; - -#define LRT_ITER_ALL_LINES_NEXT \ - while (!next_e) { \ - if (current_head == &rb->contour.first) { \ - current_head = &rb->crease.first; \ - } \ - else if (current_head == &rb->crease.first) { \ - current_head = &rb->material.first; \ - } \ - else if (current_head == &rb->material.first) { \ - current_head = &rb->edge_mark.first; \ - } \ - else if (current_head == &rb->edge_mark.first) { \ - current_head = &rb->intersection.first; \ - } \ - else if (current_head == &rb->intersection.first) { \ - current_head = &rb->floating.first; \ - } \ - else { \ - break; \ - } \ - next_e = *current_head; \ - } + LineartEdge *e; \ + for (int i = 0; i < rb->pending_edges.next; i++) { \ + e = rb->pending_edges.array[i]; + +#define LRT_ITER_ALL_LINES_NEXT ; /* Doesn't do anything now with new array setup. */ #define LRT_ITER_ALL_LINES_END \ LRT_ITER_ALL_LINES_NEXT \ @@ -121,3 +83,13 @@ void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb /* Initial bounding area row/column count, setting 4 is the simplest way algorithm could function * efficiently. */ #define LRT_BA_ROWS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 6d84f71d92f..e78c86ae196 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_common_types.h GPU_compute.h GPU_context.h GPU_debug.h @@ -189,6 +190,7 @@ set(METAL_SRC metal/mtl_backend.mm metal/mtl_context.mm metal/mtl_debug.mm + metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm @@ -197,6 +199,7 @@ set(METAL_SRC metal/mtl_common.hh metal/mtl_context.hh metal/mtl_debug.hh + metal/mtl_state.hh metal/mtl_texture.hh ) @@ -301,7 +304,13 @@ set(GLSL_SRC shaders/gpu_shader_codegen_lib.glsl - shaders/gpu_shader_geometry.glsl + shaders/common/gpu_shader_common_color_ramp.glsl + shaders/common/gpu_shader_common_color_utils.glsl + shaders/common/gpu_shader_common_curves.glsl + shaders/common/gpu_shader_common_hash.glsl + shaders/common/gpu_shader_common_math.glsl + shaders/common/gpu_shader_common_math_utils.glsl + shaders/common/gpu_shader_common_mix_rgb.glsl shaders/material/gpu_shader_material_add_shader.glsl shaders/material/gpu_shader_material_ambient_occlusion.glsl @@ -315,8 +324,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_bump.glsl shaders/material/gpu_shader_material_camera.glsl shaders/material/gpu_shader_material_clamp.glsl - shaders/material/gpu_shader_material_color_ramp.glsl - shaders/material/gpu_shader_material_color_util.glsl + shaders/material/gpu_shader_material_combine_color.glsl shaders/material/gpu_shader_material_combine_hsv.glsl shaders/material/gpu_shader_material_combine_rgb.glsl shaders/material/gpu_shader_material_combine_xyz.glsl @@ -324,7 +332,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_displacement.glsl shaders/material/gpu_shader_material_eevee_specular.glsl shaders/material/gpu_shader_material_emission.glsl - shaders/material/gpu_shader_material_float_curve.glsl shaders/material/gpu_shader_material_fractal_noise.glsl shaders/material/gpu_shader_material_fresnel.glsl shaders/material/gpu_shader_material_gamma.glsl @@ -333,7 +340,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_glossy.glsl shaders/material/gpu_shader_material_hair_info.glsl shaders/material/gpu_shader_material_hair.glsl - shaders/material/gpu_shader_material_hash.glsl shaders/material/gpu_shader_material_holdout.glsl shaders/material/gpu_shader_material_hue_sat_val.glsl shaders/material/gpu_shader_material_invert.glsl @@ -342,9 +348,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_light_path.glsl shaders/material/gpu_shader_material_mapping.glsl shaders/material/gpu_shader_material_map_range.glsl - shaders/material/gpu_shader_material_math.glsl - shaders/material/gpu_shader_material_math_util.glsl - shaders/material/gpu_shader_material_mix_rgb.glsl shaders/material/gpu_shader_material_mix_shader.glsl shaders/material/gpu_shader_material_noise.glsl shaders/material/gpu_shader_material_normal.glsl @@ -357,8 +360,8 @@ set(GLSL_SRC shaders/material/gpu_shader_material_point_info.glsl shaders/material/gpu_shader_material_principled.glsl shaders/material/gpu_shader_material_refraction.glsl - shaders/material/gpu_shader_material_rgb_curves.glsl shaders/material/gpu_shader_material_rgb_to_bw.glsl + shaders/material/gpu_shader_material_separate_color.glsl shaders/material/gpu_shader_material_separate_hsv.glsl shaders/material/gpu_shader_material_separate_rgb.glsl shaders/material/gpu_shader_material_separate_xyz.glsl @@ -381,10 +384,10 @@ set(GLSL_SRC shaders/material/gpu_shader_material_tex_wave.glsl shaders/material/gpu_shader_material_tex_white_noise.glsl shaders/material/gpu_shader_material_toon.glsl + shaders/material/gpu_shader_material_transform_utils.glsl shaders/material/gpu_shader_material_translucent.glsl shaders/material/gpu_shader_material_transparent.glsl shaders/material/gpu_shader_material_uv_map.glsl - shaders/material/gpu_shader_material_vector_curves.glsl shaders/material/gpu_shader_material_vector_displacement.glsl shaders/material/gpu_shader_material_vector_math.glsl shaders/material/gpu_shader_material_vector_rotate.glsl @@ -441,6 +444,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_SHADER_CREATE_INFOS ../draw/engines/basic/shaders/infos/basic_depth_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh ../draw/engines/overlay/shaders/infos/antialiasing_info.hh @@ -494,6 +498,7 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_2D_widget_info.hh shaders/infos/gpu_shader_3D_depth_only_info.hh shaders/infos/gpu_shader_3D_flat_color_info.hh + shaders/infos/gpu_shader_3D_image_info.hh shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh shaders/infos/gpu_shader_3D_point_info.hh shaders/infos/gpu_shader_3D_polyline_info.hh diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 061b850619f..7fe467de402 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -35,7 +35,7 @@ int GPU_max_compute_shader_storage_blocks(void); int GPU_extensions_len(void); const char *GPU_extension_get(int i); -int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size); +int GPU_texture_size_with_limit(int res); bool GPU_mip_render_workaround(void); bool GPU_depth_blitting_workaround(void); diff --git a/source/blender/gpu/GPU_common_types.h b/source/blender/gpu/GPU_common_types.h new file mode 100644 index 00000000000..8c91d60812f --- /dev/null +++ b/source/blender/gpu/GPU_common_types.h @@ -0,0 +1,18 @@ +/** \file + * \ingroup gpu + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum eGPUFrontFace { + GPU_CLOCKWISE, + GPU_COUNTERCLOCKWISE, +} eGPUFrontFace; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h index 369347447f8..5970738a9b3 100644 --- a/source/blender/gpu/GPU_legacy_stubs.h +++ b/source/blender/gpu/GPU_legacy_stubs.h @@ -23,7 +23,7 @@ #include "BLI_utildefines.h" /** - * Empty function, use for breakpoint when a deprecated + * Empty function, use for break-point when a deprecated * OpenGL function is called. */ static void gl_deprecated(void) diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index b620fe9cc9d..3460d33fe68 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -280,6 +280,16 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE, GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR, /** + * Draw a texture in 3D. Take a 3D position and a 2D texture coordinate for each vertex. + * + * Exposed via Python-API for add-ons. + * + * \param image: uniform sampler2D + * \param texCoord: in vec2 + * \param pos: in vec3 + */ + GPU_SHADER_3D_IMAGE, + /** * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex. * * \param alpha: uniform float diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 99b60351dcc..7519b3bc1cb 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -35,6 +35,19 @@ typedef enum eGPUBarrier { ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) +/* NOTE: For Metal and Vulkan only. + * TODO(Metal): Update barrier calls to use stage flags. */ +typedef enum eGPUStageBarrierBits { + GPU_BARRIER_STAGE_VERTEX = (1 << 0), + GPU_BARRIER_STAGE_FRAGMENT = (1 << 1), + GPU_BARRIER_STAGE_COMPUTE = (1 << 2), + GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT), + GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT | + GPU_BARRIER_STAGE_COMPUTE), +} eGPUStageBarrierBits; + +ENUM_OPERATORS(eGPUStageBarrierBits, GPU_BARRIER_STAGE_COMPUTE) + /** * Defines the fixed pipeline blending equation. * SRC is the output color from the shader. diff --git a/source/blender/gpu/GPU_storage_buffer.h b/source/blender/gpu/GPU_storage_buffer.h index 1478d490e23..ca6a848786b 100644 --- a/source/blender/gpu/GPU_storage_buffer.h +++ b/source/blender/gpu/GPU_storage_buffer.h @@ -47,6 +47,18 @@ void GPU_storagebuf_clear(GPUStorageBuf *ssbo, void *data); void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo); +/** + * \brief Copy a part of a vertex buffer to a storage buffer. + * + * \param ssbo: destination storage buffer + * \param src: source vertex buffer + * \param dst_offset: where to start copying to (in bytes). + * \param src_offset: where to start copying from (in bytes). + * \param copy_size: byte size of the segment to copy. + */ +void GPU_storagebuf_copy_sub_from_vertbuf( + GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index bb0912f284b..b045d908438 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -280,9 +280,9 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); /** * Fills the whole texture with the same data for all pixels. * \warning Only work for 2D texture for now. - * \warning Only clears the mip 0 of the texture. + * \warning Only clears the MIP 0 of the texture. * \param data_format: data format of the pixel data. - * \note The format is float for unorm textures. + * \note The format is float for UNORM textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index fe6ed7eaf87..722ef878271 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -165,6 +165,7 @@ void GPU_vertbuf_tag_dirty(GPUVertBuf *verts); */ void GPU_vertbuf_use(GPUVertBuf *); void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding); void GPU_vertbuf_wrap_handle(GPUVertBuf *verts, uint64_t handle); diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index c6b0d5902fe..bf8dc409cb7 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -55,7 +55,7 @@ typedef struct GPUVertAttr { /* 1 to 4 or 8 or 12 or 16 */ uint comp_len : 5; /* size in bytes, 1 to 64 */ - uint sz : 7; + uint size : 7; /* from beginning of vertex, in bytes */ uint offset : 11; /* up to GPU_VERT_ATTR_MAX_NAMES */ diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index 4ec215c2d3b..eb69a1d2635 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -33,11 +33,10 @@ int GPU_max_texture_size() return GCaps.max_texture_size; } -int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size) +int GPU_texture_size_with_limit(int res) { int size = GPU_max_texture_size(); - int reslimit = (limit_gl_texture_size && (U.glreslimit != 0)) ? min_ii(U.glreslimit, size) : - size; + int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; return min_ii(reslimit, res); } diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index c62a6416f92..055207eace8 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -50,11 +50,11 @@ void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf) r_name_buf[0] = '\0'; return; } - size_t sz = 0; + size_t len = 0; for (StringRef &name : stack) { - sz += BLI_snprintf_rlen(r_name_buf + sz, name_buf_len - sz, "%s > ", name.data()); + len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data()); } - r_name_buf[sz - 3] = '\0'; + r_name_buf[len - 3] = '\0'; } bool GPU_debug_group_match(const char *ref) diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 7dc1c739750..69467e5b28a 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -490,7 +490,7 @@ static void immEndVertex() /* and move on to the next vertex */ #endif uchar *data = imm->vertex_data + a->offset; - memcpy(data, data - imm->vertex_format.stride, a->sz); + memcpy(data, data - imm->vertex_format.stride, a->size); /* TODO: consolidate copy of adjacent attributes */ } } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 7ec6ee5183a..23028f58059 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -626,7 +626,7 @@ eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat) return mat->flag; } -/* Note: Consumes the flags. */ +/* NOTE: Consumes the flags. */ bool GPU_material_recalc_flag_get(GPUMaterial *mat) { bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index ffc2228a49b..b071ab85f3a 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -30,7 +30,7 @@ typedef struct GPUFunction { eGPUType paramtype[MAX_PARAMETER]; GPUFunctionQual paramqual[MAX_PARAMETER]; int totparam; - /* TOOD(@fclem): Clean that void pointer. */ + /* TODO(@fclem): Clean that void pointer. */ void *source; /* GPUSource */ } GPUFunction; diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 1100272b712..b92fae4a89b 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -150,6 +150,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .name = "GPU_SHADER_SIMPLE_LIGHTING", .create_info = "gpu_shader_simple_lighting", }, + [GPU_SHADER_3D_IMAGE] = + { + .name = "GPU_SHADER_3D_IMAGE", + .create_info = "gpu_shader_3D_image", + }, [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = { .name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA", diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index f69c56b5f3f..842914f5bed 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -593,7 +593,9 @@ struct GPUSource { bool is_from_material_library() const { - return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + return (filename.startswith("gpu_shader_material_") || + filename.startswith("gpu_shader_common_")) && + filename.endswith(".glsl"); } }; @@ -668,14 +670,20 @@ Vector<const char *> gpu_shader_dependency_get_resolved_source( const StringRefNull shader_source_name) { Vector<const char *> result; - GPUSource *source = g_sources->lookup(shader_source_name); - source->build(result); + GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr); + if (src == nullptr) { + std::cout << "Error source not found : " << shader_source_name << std::endl; + } + src->build(result); return result; } StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name) { - GPUSource *src = g_sources->lookup(shader_source_name); + GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr); + if (src == nullptr) { + std::cout << "Error source not found : " << shader_source_name << std::endl; + } return src->source; } diff --git a/source/blender/gpu/intern/gpu_storage_buffer.cc b/source/blender/gpu/intern/gpu_storage_buffer.cc index 806acad90fe..68020ec66f4 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer.cc +++ b/source/blender/gpu/intern/gpu_storage_buffer.cc @@ -19,6 +19,7 @@ #include "GPU_storage_buffer.h" #include "gpu_storage_buffer_private.hh" +#include "gpu_vertex_buffer_private.hh" /* -------------------------------------------------------------------- */ /** \name Creation & Deletion @@ -103,4 +104,10 @@ void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo) GPU_storagebuf_clear(ssbo, GPU_R32UI, GPU_DATA_UINT, &data); } +void GPU_storagebuf_copy_sub_from_vertbuf( + GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size) +{ + unwrap(ssbo)->copy_sub(unwrap(src), dst_offset, src_offset, copy_size); +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh index 06e7c2c0e9d..091e6c2d386 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh @@ -43,6 +43,7 @@ class StorageBuf { virtual void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) = 0; + virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0; }; /* Syntactic sugar. */ diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 2974547c858..f47970d48d1 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -199,7 +199,7 @@ void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void BLI_assert(a_idx < format->attr_len); BLI_assert(verts->data != nullptr); verts->flag |= GPU_VERTBUF_DATA_DIRTY; - memcpy(verts->data + a->offset + v_idx * format->stride, data, a->sz); + memcpy(verts->data + a->offset + v_idx * format->stride, data, a->size); } void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) @@ -208,7 +208,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) const GPUVertFormat *format = &verts->format; BLI_assert(a_idx < format->attr_len); const GPUVertAttr *a = &format->attrs[a_idx]; - const uint stride = a->sz; /* tightly packed input data */ + const uint stride = a->size; /* tightly packed input data */ verts->flag |= GPU_VERTBUF_DATA_DIRTY; GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data); } @@ -235,13 +235,13 @@ void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, c if (format->attr_len == 1 && stride == format->stride) { /* we can copy it all at once */ - memcpy(verts->data, data, vertex_len * a->sz); + memcpy(verts->data, data, vertex_len * a->size); } else { /* we must copy it per vertex */ for (uint v = 0; v < vertex_len; v++) { memcpy( - verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->sz); + verts->data + a->offset + v * format->stride, (const uchar *)data + v * stride, a->size); } } } @@ -256,7 +256,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw verts->flag |= GPU_VERTBUF_DATA_DIRTY; verts->flag &= ~GPU_VERTBUF_DATA_UPLOADED; - access->size = a->sz; + access->size = a->size; access->stride = format->stride; access->data = (uchar *)verts->data + a->offset; access->data_init = access->data; @@ -328,6 +328,11 @@ void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) unwrap(verts)->bind_as_ssbo(binding); } +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_texture(binding); +} + void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, const void *data) { unwrap(verts)->update_sub(start, len, data); diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index e5b70de9dfa..7a0b53cf958 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -51,6 +51,7 @@ class VertBuf { void resize(uint vert_len); void upload(); virtual void bind_as_ssbo(uint binding) = 0; + virtual void bind_as_texture(uint binding) = 0; virtual void wrap_handle(uint64_t handle) = 0; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index a9ac191754b..59ae862aa51 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -49,7 +49,7 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static uint comp_sz(GPUVertCompType type) +static uint comp_size(GPUVertCompType type) { #if TRUST_NO_ONE assert(type <= GPU_COMP_F32); /* other types have irregular sizes (not bytes) */ @@ -58,12 +58,12 @@ static uint comp_sz(GPUVertCompType type) return sizes[type]; } -static uint attr_sz(const GPUVertAttr *a) +static uint attr_size(const GPUVertAttr *a) { if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -71,7 +71,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); + uint c = comp_size(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -156,7 +156,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ - attr->sz = attr_sz(attr); + attr->size = attr_size(attr); attr->offset = 0; /* offsets & stride are calculated later (during pack) */ attr->fetch_mode = fetch_mode; @@ -294,13 +294,13 @@ uint padding(uint offset, uint alignment) } #if PACK_DEBUG -static void show_pack(uint a_idx, uint sz, uint pad) +static void show_pack(uint a_idx, uint size, uint pad) { const char c = 'A' + a_idx; for (uint i = 0; i < pad; i++) { putchar('-'); } - for (uint i = 0; i < sz; i++) { + for (uint i = 0; i < size; i++) { putchar(c); } } @@ -310,10 +310,10 @@ void VertexFormat_pack(GPUVertFormat *format) { GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; - uint offset = a0->sz; + uint offset = a0->size; #if PACK_DEBUG - show_pack(0, a0->sz, 0); + show_pack(0, a0->size, 0); #endif for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) { @@ -321,10 +321,10 @@ void VertexFormat_pack(GPUVertFormat *format) uint mid_padding = padding(offset, attr_align(a)); offset += mid_padding; a->offset = offset; - offset += a->sz; + offset += a->size; #if PACK_DEBUG - show_pack(a_idx, a->sz, mid_padding); + show_pack(a_idx, a->size, mid_padding); #endif } diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl index ecb2dddcd63..9fd54f3f31f 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl @@ -7,4 +7,4 @@ in vec2 texCoord_interp; void main() { gl_FragDepth = textureLod(source_data, texCoord_interp, mip).r; -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl index 99661a760f0..7483343503f 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl @@ -10,4 +10,4 @@ void main() uint stencil = (val >> 24) & 0xFFu; uint depth = (val)&0xFFFFFFu; gl_FragDepth = float(depth) / float(0xFFFFFFu); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl index 15271ab2cdd..75d42c57f73 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl @@ -9,4 +9,4 @@ void main() uint val = textureLod(source_data, texCoord_interp, mip).r; uint depth = (val) & (0xFFFFFFFFu); gl_FragDepth = float(depth) / float(0xFFFFFFFFu); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl index 092ae45b719..faae68d2f55 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl @@ -30,4 +30,4 @@ void main() texCoord_interp = tex.zy; } gl_Position = vec4(rect.xy, 0.0f, 1.0f); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index aa198482291..1849a04ea48 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -7,8 +7,10 @@ #include "gpu_context_private.hh" +#include "GPU_common_types.h" #include "GPU_context.h" +#include "mtl_capabilities.hh" #include "mtl_texture.hh" #include <Cocoa/Cocoa.h> @@ -21,6 +23,112 @@ namespace blender::gpu { +class MTLShader; +class MTLUniformBuf; +class MTLBuffer; + +/* Depth Stencil State */ +typedef struct MTLContextDepthStencilState { + + /* Depth State. */ + bool depth_write_enable; + bool depth_test_enabled; + float depth_range_near; + float depth_range_far; + MTLCompareFunction depth_function; + float depth_bias; + float depth_slope_scale; + bool depth_bias_enabled_for_points; + bool depth_bias_enabled_for_lines; + bool depth_bias_enabled_for_tris; + + /* Stencil State. */ + bool stencil_test_enabled; + unsigned int stencil_read_mask; + unsigned int stencil_write_mask; + unsigned int stencil_ref; + MTLCompareFunction stencil_func; + + MTLStencilOperation stencil_op_front_stencil_fail; + MTLStencilOperation stencil_op_front_depth_fail; + MTLStencilOperation stencil_op_front_depthstencil_pass; + + MTLStencilOperation stencil_op_back_stencil_fail; + MTLStencilOperation stencil_op_back_depth_fail; + MTLStencilOperation stencil_op_back_depthstencil_pass; + + /* Frame-buffer State -- We need to mark this, in case stencil state remains unchanged, + * but attachment state has changed. */ + bool has_depth_target; + bool has_stencil_target; + + /* TODO(Metal): Consider optimizing this function using memcmp. + * Un-used, but differing, stencil state leads to over-generation + * of state objects when doing trivial compare. */ + inline bool operator==(const MTLContextDepthStencilState &other) const + { + bool depth_state_equality = (has_depth_target == other.has_depth_target && + depth_write_enable == other.depth_write_enable && + depth_test_enabled == other.depth_test_enabled && + depth_function == other.depth_function); + + bool stencil_state_equality = true; + if (has_stencil_target) { + stencil_state_equality = + (has_stencil_target == other.has_stencil_target && + stencil_test_enabled == other.stencil_test_enabled && + stencil_op_front_stencil_fail == other.stencil_op_front_stencil_fail && + stencil_op_front_depth_fail == other.stencil_op_front_depth_fail && + stencil_op_front_depthstencil_pass == other.stencil_op_front_depthstencil_pass && + stencil_op_back_stencil_fail == other.stencil_op_back_stencil_fail && + stencil_op_back_depth_fail == other.stencil_op_back_depth_fail && + stencil_op_back_depthstencil_pass == other.stencil_op_back_depthstencil_pass && + stencil_func == other.stencil_func && stencil_read_mask == other.stencil_read_mask && + stencil_write_mask == other.stencil_write_mask); + } + + return depth_state_equality && stencil_state_equality; + } + + /* Depth stencil state will get hashed in order to prepare + * MTLDepthStencilState objects. The hash should comprise of + * all elements which fill the MTLDepthStencilDescriptor. + * These are bound when [rec setDepthStencilState:...] is called. + * Depth bias and stencil reference value are set dynamically on the RenderCommandEncoder: + * - setStencilReferenceValue: + * - setDepthBias:slopeScale:clamp: + */ + inline std::size_t hash() const + { + std::size_t boolean_bitmask = (this->depth_write_enable ? 1 : 0) | + ((this->depth_test_enabled ? 1 : 0) << 1) | + ((this->depth_bias_enabled_for_points ? 1 : 0) << 2) | + ((this->depth_bias_enabled_for_lines ? 1 : 0) << 3) | + ((this->depth_bias_enabled_for_tris ? 1 : 0) << 4) | + ((this->stencil_test_enabled ? 1 : 0) << 5) | + ((this->has_depth_target ? 1 : 0) << 6) | + ((this->has_stencil_target ? 1 : 0) << 7); + + std::size_t stencilop_bitmask = ((std::size_t)this->stencil_op_front_stencil_fail) | + ((std::size_t)this->stencil_op_front_depth_fail << 3) | + ((std::size_t)this->stencil_op_front_depthstencil_pass << 6) | + ((std::size_t)this->stencil_op_back_stencil_fail << 9) | + ((std::size_t)this->stencil_op_back_depth_fail << 12) | + ((std::size_t)this->stencil_op_back_depthstencil_pass << 15); + + std::size_t main_hash = (std::size_t)this->depth_function; + if (this->has_stencil_target) { + main_hash += (std::size_t)(this->stencil_read_mask & 0xFF) << 8; + main_hash += (std::size_t)(this->stencil_write_mask & 0xFF) << 16; + } + main_hash ^= (std::size_t)this->stencil_func << 16; + main_hash ^= stencilop_bitmask; + + std::size_t final_hash = (main_hash << 8) | boolean_bitmask; + return final_hash; + } +} MTLContextDepthStencilState; + typedef struct MTLContextTextureUtils { /* Depth Update Utilities */ @@ -108,11 +216,149 @@ typedef struct MTLContextTextureUtils { } MTLContextTextureUtils; +/* Structs containing information on current binding state for textures and samplers. */ +typedef struct MTLTextureBinding { + bool used; + + /* Same value as index in bindings array. */ + unsigned int texture_slot_index; + gpu::MTLTexture *texture_resource; + +} MTLTextureBinding; + +typedef struct MTLSamplerBinding { + bool used; + MTLSamplerState state; + + bool operator==(MTLSamplerBinding const &other) const + { + return (used == other.used && state == other.state); + } +} MTLSamplerBinding; + +/* Combined sampler state configuration for Argument Buffer caching. */ +struct MTLSamplerArray { + unsigned int num_samplers; + /* MTLSamplerState permutations between 0..256 - slightly more than a byte. */ + MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS]; + id<MTLSamplerState> mtl_sampler[MTL_MAX_TEXTURE_SLOTS]; + + inline bool operator==(const MTLSamplerArray &other) const + { + if (this->num_samplers != other.num_samplers) { + return false; + } + return (memcmp(this->mtl_sampler_flags, + other.mtl_sampler_flags, + sizeof(MTLSamplerState) * this->num_samplers) == 0); + } + + inline uint32_t hash() const + { + uint32_t hash = this->num_samplers; + for (int i = 0; i < this->num_samplers; i++) { + hash ^= (uint32_t)this->mtl_sampler_flags[i] << (i % 3); + } + return hash; + } +}; + +typedef enum MTLPipelineStateDirtyFlag { + MTL_PIPELINE_STATE_NULL_FLAG = 0, + /* Whether we need to call setViewport. */ + MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0), + /* Whether we need to call setScissor.*/ + MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1), + /* Whether we need to update/rebind active depth stencil state. */ + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2), + /* Whether we need to update/rebind active PSO. */ + MTL_PIPELINE_STATE_PSO_FLAG = (1 << 3), + /* Whether we need to update the frontFacingWinding state. */ + MTL_PIPELINE_STATE_FRONT_FACING_FLAG = (1 << 4), + /* Whether we need to update the culling state. */ + MTL_PIPELINE_STATE_CULLMODE_FLAG = (1 << 5), + /* Full pipeline state needs applying. Occurs when beginning a new render pass. */ + MTL_PIPELINE_STATE_ALL_FLAG = + (MTL_PIPELINE_STATE_VIEWPORT_FLAG | MTL_PIPELINE_STATE_SCISSOR_FLAG | + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG | MTL_PIPELINE_STATE_PSO_FLAG | + MTL_PIPELINE_STATE_FRONT_FACING_FLAG | MTL_PIPELINE_STATE_CULLMODE_FLAG) +} MTLPipelineStateDirtyFlag; + +/* Ignore full flag bit-mask `MTL_PIPELINE_STATE_ALL_FLAG`. */ +ENUM_OPERATORS(MTLPipelineStateDirtyFlag, MTL_PIPELINE_STATE_CULLMODE_FLAG); + +typedef struct MTLUniformBufferBinding { + bool bound; + MTLUniformBuf *ubo; +} MTLUniformBufferBinding; + typedef struct MTLContextGlobalShaderPipelineState { - /* ..TODO(Metal): More elements to be added as backend fleshed out.. */ + bool initialised; + + /* Whether the pipeline state has been modified since application. + * `dirty_flags` is a bitmask of the types of state which have been updated. + * This is in order to optimize calls and only re-apply state as needed. + * Some state parameters are dynamically applied on the RenderCommandEncoder, + * others may be encapsulated in GPU-resident state objects such as + * MTLDepthStencilState or MTLRenderPipelineState. */ + bool dirty; + MTLPipelineStateDirtyFlag dirty_flags; + + /* Shader resources. */ + MTLShader *null_shader; + + /* Active Shader State. */ + MTLShader *active_shader; + + /* Global Uniform Buffers. */ + MTLUniformBufferBinding ubo_bindings[MTL_MAX_UNIFORM_BUFFER_BINDINGS]; + + /* Context Texture bindings. */ + MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS]; + MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS]; + + /*** --- Render Pipeline State --- ***/ + /* Track global render pipeline state for the current context. The functions in GPU_state.h + * modify these parameters. Certain values, tagged [PSO], are parameters which are required to be + * passed into PSO creation, rather than dynamic state functions on the RenderCommandEncoder. + */ - /*** DATA and IMAGE access state ***/ + /* Blending State. */ + MTLColorWriteMask color_write_mask; /* [PSO] */ + bool blending_enabled; /* [PSO] */ + MTLBlendOperation alpha_blend_op; /* [PSO] */ + MTLBlendOperation rgb_blend_op; /* [PSO] */ + MTLBlendFactor dest_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor dest_rgb_blend_factor; /* [PSO] */ + MTLBlendFactor src_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor src_rgb_blend_factor; /* [PSO] */ + + /* Culling State. */ + bool culling_enabled; + eGPUFaceCullTest cull_mode; + eGPUFrontFace front_face; + + /* Depth State. */ + MTLContextDepthStencilState depth_stencil_state; + + /* Viewport/Scissor Region. */ + int viewport_offset_x; + int viewport_offset_y; + int viewport_width; + int viewport_height; + bool scissor_enabled; + int scissor_x; + int scissor_y; + int scissor_width; + int scissor_height; + + /* Image data access state. */ uint unpack_row_length; + + /* Render parameters. */ + float point_size = 1.0f; + float line_width = 1.0f; + } MTLContextGlobalShaderPipelineState; /* Metal Buffer */ @@ -127,8 +373,8 @@ typedef struct MTLTemporaryBufferRange { bool requires_flush(); } MTLTemporaryBufferRange; -/** MTLContext -- Core render loop and state management **/ -/* Note(Metal): Partial MTLContext stub to provide wrapper functionality +/** MTLContext -- Core render loop and state management. **/ +/* NOTE(Metal): Partial MTLContext stub to provide wrapper functionality * for work-in-progress MTL* classes. */ class MTLContext : public Context { @@ -138,8 +384,24 @@ class MTLContext : public Context { /* Compute and specialization caches. */ MTLContextTextureUtils texture_utils_; + /* Texture Samplers. */ + /* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */ + id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX] = {0}; + id<MTLSamplerState> default_sampler_state_ = nil; + + /* When texture sampler count exceeds the resource bind limit, an + * argument buffer is used to pass samplers to the shader. + * Each unique configurations of multiple samplers can be cached, so as to not require + * re-generation. `samplers_` stores the current list of bound sampler objects. + * `cached_sampler_buffers_` is a cache of encoded argument buffers which can be re-used. */ + MTLSamplerArray samplers_; + blender::Map<MTLSamplerArray, gpu::MTLBuffer *> cached_sampler_buffers_; + public: - /* METAL API Resource Handles. */ + /* Shaders and Pipeline state. */ + MTLContextGlobalShaderPipelineState pipeline_state; + + /* Metal API Resource Handles. */ id<MTLCommandQueue> queue = nil; id<MTLDevice> device = nil; @@ -160,24 +422,40 @@ class MTLContext : public Context { void debug_group_begin(const char *name, int index) override; void debug_group_end(void) override; - /*** Context Utility functions */ + /*** MTLContext Utility functions. */ /* * All below functions modify the global state for the context, controlling the flow of * rendering, binding resources, setting global state, resource management etc; */ - /* Metal Context Core functions */ - /* Command Buffer Management */ + /* Metal Context Core functions. */ + /* Command Buffer Management. */ id<MTLCommandBuffer> get_active_command_buffer(); - /* Render Pass State and Management */ + /* Render Pass State and Management. */ void begin_render_pass(); void end_render_pass(); - - /* Shaders and Pipeline state */ - MTLContextGlobalShaderPipelineState pipeline_state; - - /* Texture utilities */ + bool is_render_pass_active(); + + /* Texture Binding. */ + void texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit); + void sampler_bind(MTLSamplerState, unsigned int sampler_unit); + void texture_unbind(gpu::MTLTexture *mtl_texture); + void texture_unbind_all(void); + id<MTLSamplerState> get_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> generate_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> get_default_sampler_state(); + + /* Metal Context pipeline state. */ + void pipeline_state_init(void); + MTLShader *get_active_shader(void); + + /* State assignment. */ + void set_viewport(int origin_x, int origin_y, int width, int height); + void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height); + void set_scissor_enabled(bool scissor_enabled); + + /* Texture utilities. */ MTLContextTextureUtils &get_texture_utils() { return this->texture_utils_; diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 18ed38c373d..94f5682b11b 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -5,6 +5,11 @@ */ #include "mtl_context.hh" #include "mtl_debug.hh" +#include "mtl_state.hh" + +#include "DNA_userdef_types.h" + +#include "GPU_capabilities.h" using namespace blender; using namespace blender::gpu; @@ -44,6 +49,9 @@ MTLContext::MTLContext(void *ghost_window) /* Init debug. */ debug::mtl_debug_init(); + /* Initialize Metal modules. */ + this->state_manager = new MTLStateManager(this); + /* TODO(Metal): Implement. */ } @@ -98,6 +106,234 @@ void MTLContext::end_render_pass() /* TODO(Metal): Implement. */ } +bool MTLContext::is_render_pass_active() +{ + /* TODO(Metal): Implement. */ + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Global Context State + * \{ */ + +/* Metal Context Pipeline State. */ +void MTLContext::pipeline_state_init() +{ + /*** Initialize state only once. ***/ + if (!this->pipeline_state.initialised) { + this->pipeline_state.initialised = true; + this->pipeline_state.active_shader = NULL; + + /* Clear bindings state. */ + for (int t = 0; t < GPU_max_textures(); t++) { + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_slot_index = t; + this->pipeline_state.texture_bindings[t].texture_resource = NULL; + } + for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) { + this->pipeline_state.sampler_bindings[s].used = false; + } + for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) { + this->pipeline_state.ubo_bindings[u].bound = false; + this->pipeline_state.ubo_bindings[u].ubo = NULL; + } + } + + /*** State defaults -- restored by GPU_state_init. ***/ + /* Clear blending State. */ + this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen | + MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha; + this->pipeline_state.blending_enabled = false; + this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero; + this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero; + this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne; + this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne; + + /* Viewport and scissor. */ + this->pipeline_state.viewport_offset_x = 0; + this->pipeline_state.viewport_offset_y = 0; + this->pipeline_state.viewport_width = 0; + this->pipeline_state.viewport_height = 0; + this->pipeline_state.scissor_x = 0; + this->pipeline_state.scissor_y = 0; + this->pipeline_state.scissor_width = 0; + this->pipeline_state.scissor_height = 0; + this->pipeline_state.scissor_enabled = false; + + /* Culling State. */ + this->pipeline_state.culling_enabled = false; + this->pipeline_state.cull_mode = GPU_CULL_NONE; + this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE; + + /* DATA and IMAGE access state. */ + this->pipeline_state.unpack_row_length = 0; + + /* Depth State. */ + this->pipeline_state.depth_stencil_state.depth_write_enable = false; + this->pipeline_state.depth_stencil_state.depth_test_enabled = false; + this->pipeline_state.depth_stencil_state.depth_range_near = 0.0; + this->pipeline_state.depth_stencil_state.depth_range_far = 1.0; + this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.depth_bias = 0.0; + this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false; + + /* Stencil State. */ + this->pipeline_state.depth_stencil_state.stencil_test_enabled = false; + this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_ref = 0; + this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass = + MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass = + MTLStencilOperationKeep; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit) +{ + BLI_assert(this); + BLI_assert(mtl_texture); + + if (texture_unit < 0 || texture_unit >= GPU_max_textures() || + texture_unit >= MTL_MAX_TEXTURE_SLOTS) { + MTL_LOG_WARNING("Attempting to bind texture '%s' to invalid texture unit %d\n", + mtl_texture->get_name(), + texture_unit); + BLI_assert(false); + return; + } + + /* Bind new texture. */ + this->pipeline_state.texture_bindings[texture_unit].texture_resource = mtl_texture; + this->pipeline_state.texture_bindings[texture_unit].used = true; + mtl_texture->is_bound_ = true; +} + +void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit) +{ + BLI_assert(this); + if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() || + sampler_unit >= MTL_MAX_SAMPLER_SLOTS) { + MTL_LOG_WARNING("Attempting to bind sampler to invalid sampler unit %d\n", sampler_unit); + BLI_assert(false); + return; + } + + /* Apply binding. */ + this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state}; +} + +void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture) +{ + BLI_assert(mtl_texture); + + /* Iterate through textures in state and unbind. */ + for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) { + if (this->pipeline_state.texture_bindings[i].texture_resource == mtl_texture) { + this->pipeline_state.texture_bindings[i].texture_resource = nullptr; + this->pipeline_state.texture_bindings[i].used = false; + } + } + + /* Locally unbind texture. */ + mtl_texture->is_bound_ = false; +} + +void MTLContext::texture_unbind_all() +{ + /* Iterate through context's bound textures. */ + for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) { + if (this->pipeline_state.texture_bindings[t].used && + this->pipeline_state.texture_bindings[t].texture_resource) { + + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_resource = nullptr; + } + } +} + +id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state) +{ + BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX); + return this->sampler_state_cache_[(unsigned int)sampler_state]; +} + +id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state) +{ + /* Check if sampler already exists for given state. */ + id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state]; + if (st != nil) { + return st; + } + else { + MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init]; + descriptor.normalizedCoordinates = true; + + MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ? + MTLSamplerAddressModeClampToBorderColor : + MTLSamplerAddressModeClampToEdge; + descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.borderColor = MTLSamplerBorderColorTransparentBlack; + descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? + MTLSamplerMipFilterLinear : + MTLSamplerMipFilterNotMipmapped; + descriptor.lodMinClamp = -1000; + descriptor.lodMaxClamp = 1000; + float aniso_filter = max_ff(16, U.anisotropic_filter); + descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1; + descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ? + MTLCompareFunctionLessEqual : + MTLCompareFunctionAlways; + descriptor.supportArgumentBuffers = true; + + id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor]; + this->sampler_state_cache_[(unsigned int)sampler_state] = state; + + BLI_assert(state != nil); + [descriptor autorelease]; + return state; + } +} + +id<MTLSamplerState> MTLContext::get_default_sampler_state() +{ + if (this->default_sampler_state_ == nil) { + this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE); + } + return this->default_sampler_state_; +} + /** \} */ } // blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh new file mode 100644 index 00000000000..f2d85f9648b --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.hh @@ -0,0 +1,73 @@ +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "GPU_state.h" +#include "gpu_state_private.hh" + +namespace blender::gpu { + +/* Forward Declarations. */ +class MTLContext; + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Metal Implementation. + **/ +class MTLStateManager : public StateManager { + public: + private: + /* Current state of the associated MTLContext. + * Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + MTLContext *context_; + + public: + MTLStateManager(MTLContext *ctx); + + void apply_state(void) override; + void force_state(void) override; + + void issue_barrier(eGPUBarrier barrier_bits) override; + + void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override; + void texture_unbind(Texture *tex) override; + void texture_unbind_all(void) override; + + void image_bind(Texture *tex, int unit) override; + void image_unbind(Texture *tex) override; + void image_unbind_all(void) override; + + void texture_unpack_row_length_set(uint len) override; + + private: + void set_write_mask(const eGPUWriteMask value); + void set_depth_test(const eGPUDepthTest value); + void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + void set_clip_distances(const int new_dist_len, const int old_dist_len); + void set_logic_op(const bool enable); + void set_facing(const bool invert); + void set_backface_culling(const eGPUFaceCullTest test); + void set_provoking_vert(const eGPUProvokingVertex vert); + void set_shadow_bias(const bool enable); + void set_blend(const eGPUBlend value); + + void set_state(const GPUState &state); + void set_mutable_state(const GPUStateMutable &state); + + /* METAL State utility functions. */ + void mtl_state_init(void); + void mtl_depth_range(float near, float far); + void mtl_stencil_mask(unsigned int mask); + void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, unsigned int mask); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager") +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm new file mode 100644 index 00000000000..5f52bc55f72 --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.mm @@ -0,0 +1,675 @@ +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" +#include "BLI_math_bits.h" + +#include "GPU_framebuffer.h" + +#include "mtl_context.hh" +#include "mtl_state.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name MTLStateManager + * \{ */ + +void MTLStateManager::mtl_state_init(void) +{ + BLI_assert(this->context_); + this->context_->pipeline_state_init(); +} + +MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager() +{ + /* Initialize State. */ + this->context_ = ctx; + mtl_state_init(); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void MTLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + /* TODO(Metal): Enable after integration of MTLFrameBuffer. */ + /* static_cast<MTLFrameBuffer *>(this->context_->active_fb)->apply_state(); */ +}; + +void MTLStateManager::force_state(void) +{ + /* Little exception for clip distances since they need to keep the old count correct. */ + uint32_t clip_distances = current_.clip_distances; + current_ = ~this->state; + current_.clip_distances = clip_distances; + current_mutable_ = ~this->mutable_state; + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); +}; + +void MTLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling((eGPUFaceCullTest)state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove (Following GLState). */ + if (changed.polygon_smooth) { + /* NOTE: Unsupported in Metal. */ + } + if (changed.line_smooth) { + /* NOTE: Unsupported in Metal. */ + } + + current_ = state; +} + +void MTLStateManager::mtl_depth_range(float near, float far) +{ + BLI_assert(this->context_); + BLI_assert(near >= 0.0 && near < 1.0); + BLI_assert(far > 0.0 && far <= 1.0); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_range_near = near; + ds_state.depth_range_far = far; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_VIEWPORT_FLAG; +} + +void MTLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (float_as_uint(changed.point_size) != 0) { + pipeline_state.point_size = state.point_size; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.line_width != 0) { + pipeline_state.line_width = state.line_width; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + mtl_depth_range(state.depth_range[0], state.depth_range[1]); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State setting functions + * \{ */ + +void MTLStateManager::set_write_mask(const eGPUWriteMask value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.depth_write_enable = ((value & GPU_WRITE_DEPTH) != 0); + pipeline_state.color_write_mask = + (((value & GPU_WRITE_RED) != 0) ? MTLColorWriteMaskRed : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_GREEN) != 0) ? MTLColorWriteMaskGreen : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_BLUE) != 0) ? MTLColorWriteMaskBlue : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_ALPHA) != 0) ? MTLColorWriteMaskAlpha : MTLColorWriteMaskNone); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; +} + +static MTLCompareFunction gpu_depth_function_to_metal(eGPUDepthTest depth_func) +{ + switch (depth_func) { + case GPU_DEPTH_NONE: + return MTLCompareFunctionNever; + case GPU_DEPTH_LESS: + return MTLCompareFunctionLess; + case GPU_DEPTH_EQUAL: + return MTLCompareFunctionEqual; + case GPU_DEPTH_LESS_EQUAL: + return MTLCompareFunctionLessEqual; + case GPU_DEPTH_GREATER: + return MTLCompareFunctionGreater; + case GPU_DEPTH_GREATER_EQUAL: + return MTLCompareFunctionGreaterEqual; + case GPU_DEPTH_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Invalid eGPUDepthTest"); + break; + } + return MTLCompareFunctionAlways; +} + +static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func) +{ + switch (stencil_func) { + case GPU_STENCIL_NONE: + return MTLCompareFunctionAlways; + case GPU_STENCIL_EQUAL: + return MTLCompareFunctionEqual; + case GPU_STENCIL_NEQUAL: + return MTLCompareFunctionNotEqual; + case GPU_STENCIL_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Unrecognised eGPUStencilTest function"); + break; + } + return MTLCompareFunctionAlways; +} + +void MTLStateManager::set_depth_test(const eGPUDepthTest value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_test_enabled = (value != GPU_DEPTH_NONE); + ds_state.depth_function = gpu_depth_function_to_metal(value); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_mask(unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_write_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_set_func(eGPUStencilTest stencil_func, + int ref, + unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.stencil_func = gpu_stencil_func_to_metal(stencil_func); + ds_state.stencil_ref = ref; + ds_state.stencil_read_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op_separate(MTLContext *context, + eGPUFaceCullTest face, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + BLI_assert(context); + MTLContextGlobalShaderPipelineState &pipeline_state = context->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + if (face == GPU_CULL_FRONT) { + ds_state.stencil_op_front_stencil_fail = stencil_fail; + ds_state.stencil_op_front_depth_fail = depth_test_fail; + ds_state.stencil_op_front_depthstencil_pass = depthstencil_pass; + } + else if (face == GPU_CULL_BACK) { + ds_state.stencil_op_back_stencil_fail = stencil_fail; + ds_state.stencil_op_back_depth_fail = depth_test_fail; + ds_state.stencil_op_back_depthstencil_pass = depthstencil_pass; + } + + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op(MTLContext *context, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + mtl_stencil_set_op_separate( + context, GPU_CULL_FRONT, stencil_fail, depth_test_fail, depthstencil_pass); + mtl_stencil_set_op_separate( + context, GPU_CULL_BACK, stencil_fail, depth_test_fail, depthstencil_pass); +} + +void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationReplace); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap, + MTLStencilOperationKeep); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap, + MTLStencilOperationKeep); + break; + case GPU_STENCIL_OP_NONE: + default: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationKeep); + } + + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_test_enabled = (test != GPU_STENCIL_NONE); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + if (test == GPU_STENCIL_NONE) { + mtl_stencil_mask(0x00); + mtl_stencil_set_func(GPU_STENCIL_ALWAYS, 0x00, 0x00); + } + else { + mtl_stencil_mask(state.stencil_write_mask); + mtl_stencil_set_func(test, state.stencil_reference, state.stencil_compare_mask); + } +} + +void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + /* TODO(Metal): Support Clip distances in METAL. Clip distance + * assignment via shader is supported, but global clip-states require + * support. */ +} + +void MTLStateManager::set_logic_op(const bool enable) +{ + /* NOTE(Metal): Logic Operations not directly supported. */ +} + +void MTLStateManager::set_facing(const bool invert) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State -- opposite of GL, as METAL default is GPU_CLOCKWISE, GL default is + * COUNTERCLOCKWISE. This needs to be the inverse of the default. */ + pipeline_state.front_face = (invert) ? GPU_COUNTERCLOCKWISE : GPU_CLOCKWISE; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_FRONT_FACING_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State. */ + pipeline_state.culling_enabled = (test != GPU_CULL_NONE); + pipeline_state.cull_mode = test; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_CULLMODE_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + /* NOTE(Metal): Provoking vertex is not a feature in the Metal API. + * Shaders are handled on a case-by-case basis using a modified vertex shader. + * For example, wireframe rendering and edit-mesh shaders utilize an SSBO-based + * vertex fetching mechanism which considers the inverse convention for flat + * shading, to ensure consistent results with OpenGL. */ +} + +void MTLStateManager::set_shadow_bias(const bool enable) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + /* Apply State. */ + if (enable) { + ds_state.depth_bias_enabled_for_lines = true; + ds_state.depth_bias_enabled_for_tris = true; + ds_state.depth_bias = 2.0f; + ds_state.depth_slope_scale = 1.0f; + } + else { + ds_state.depth_bias_enabled_for_lines = false; + ds_state.depth_bias_enabled_for_tris = false; + ds_state.depth_bias = 0.0f; + ds_state.depth_slope_scale = 0.0f; + } + + /* Mark Dirty - Ensure context updates depth-stencil state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + MTLBlendFactor src_rgb; + MTLBlendFactor dst_rgb; + MTLBlendFactor src_alpha; + MTLBlendFactor dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but pre-multiply the source RGB by it. */ + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = MTLBlendFactorDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorDestinationAlpha; + dst_alpha = MTLBlendFactorZero; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = MTLBlendFactorOneMinusDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_OIT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorSourceAlpha; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_UNDER_PREMUL: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOneMinusDestinationAlpha; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorSource1Color; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorSource1Alpha; + break; + } + } + + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (value == GPU_BLEND_SUBTRACT) { + pipeline_state.rgb_blend_op = MTLBlendOperationReverseSubtract; + pipeline_state.alpha_blend_op = MTLBlendOperationReverseSubtract; + } + else { + pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + } + + /* Apply State. */ + pipeline_state.blending_enabled = (value != GPU_BLEND_NONE); + pipeline_state.src_rgb_blend_factor = src_rgb; + pipeline_state.dest_rgb_blend_factor = dst_rgb; + pipeline_state.src_alpha_blend_factor = src_alpha; + pipeline_state.dest_alpha_blend_factor = dst_alpha; + + /* Mark Dirty - Ensure context updates PSOs between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + pipeline_state.dirty = true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Memory barrier + * \{ */ + +/* NOTE(Metal): Granular option for specifying before/after stages for a barrier + * Would be a useful feature. */ +/*void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits, + eGPUStageBarrierBits before_stages, + eGPUStageBarrierBits after_stages) */ +void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits) +{ + /* NOTE(Metal): The Metal API implicitly tracks dependencies between resources. + * Memory barriers and execution barriers (Fences/Events) can be used to coordinate + * this explicitly, however, in most cases, the driver will be able to + * resolve these dependencies automatically. + * For untracked resources, such as MTLHeap's, explicit barriers are necessary. */ + eGPUStageBarrierBits before_stages = GPU_BARRIER_STAGE_ANY; + eGPUStageBarrierBits after_stages = GPU_BARRIER_STAGE_ANY; + + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + if (ctx->is_render_pass_active()) { + + /* Apple Silicon does not support memory barriers. + * We do not currently need these due to implicit API guarantees. + * NOTE(Metal): MTLFence/MTLEvent may be required to synchronize work if + * untracked resources are ever used. */ + if ([ctx->device hasUnifiedMemory]) { + return; + } + + /* Issue barrier. */ + /* TODO(Metal): To be completed pending implementation of RenderCommandEncoder management. */ + id<MTLRenderCommandEncoder> rec = nil; // ctx->get_active_render_command_encoder(); + BLI_assert(rec); + + /* Only supporting Metal on 10.15 onward anyway - Check required for warnings. */ + if (@available(macOS 10.14, *)) { + MTLBarrierScope scope = 0; + if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS || + barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { + scope = scope | MTLBarrierScopeTextures | MTLBarrierScopeRenderTargets; + } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE || + barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY || + barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) { + scope = scope | MTLBarrierScopeBuffers; + } + + MTLRenderStages before_stage_flags = 0; + MTLRenderStages after_stage_flags = 0; + if (before_stages & GPU_BARRIER_STAGE_VERTEX && + !(before_stages & GPU_BARRIER_STAGE_FRAGMENT)) { + before_stage_flags = before_stage_flags | MTLRenderStageVertex; + } + if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) { + before_stage_flags = before_stage_flags | MTLRenderStageFragment; + } + if (after_stages & GPU_BARRIER_STAGE_VERTEX) { + after_stage_flags = after_stage_flags | MTLRenderStageVertex; + } + if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) { + after_stage_flags = MTLRenderStageFragment; + } + + if (scope != 0) { + [rec memoryBarrierWithScope:scope + afterStages:after_stage_flags + beforeStages:before_stage_flags]; + } + } + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLStateManager::texture_unpack_row_length_set(uint len) +{ + /* Set source image row data stride when uploading image data to the GPU. */ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->pipeline_state.unpack_row_length = len; +} + +void MTLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler_type, int unit) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + if (unit >= 0) { + ctx->texture_bind(mtl_tex, unit); + + /* Fetching textures default sampler configuration and applying + * eGPUSampler State on top. This path exists to support + * Any of the sampler state which is associated with the + * texture itself such as min/max mip levels. */ + MTLSamplerState sampler = mtl_tex->get_sampler_state(); + sampler.state = sampler_type; + + ctx->sampler_bind(sampler, unit); + } +} + +void MTLStateManager::texture_unbind(Texture *tex_) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->texture_unbind(mtl_tex); +} + +void MTLStateManager::texture_unbind_all(void) +{ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + BLI_assert(ctx); + ctx->texture_unbind_all(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Binding (from image load store) + * \{ */ + +void MTLStateManager::image_bind(Texture *tex_, int unit) +{ + this->texture_bind(tex_, GPU_SAMPLER_DEFAULT, unit); +} + +void MTLStateManager::image_unbind(Texture *tex_) +{ + this->texture_unbind(tex_); +} + +void MTLStateManager::image_unbind_all(void) +{ + this->texture_unbind_all(); +} + +/** \} */ + +} // blender::gpu diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index e013bb5321a..b820256ec36 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -47,16 +47,13 @@ struct TextureUpdateRoutineSpecialisation { (component_count_input == other.component_count_input) && (component_count_output == other.component_count_output)); } -}; -template<> struct blender::DefaultHash<TextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const TextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; + blender::DefaultHash<std::string> string_hasher; return (uint64_t)string_hasher( - key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + key.component_count_output)); + this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + this->component_count_output)); } }; @@ -78,12 +75,10 @@ struct DepthTextureUpdateRoutineSpecialisation { { return ((data_mode == other.data_mode)); } -}; -template<> struct blender::DefaultHash<DepthTextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const DepthTextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - return (uint64_t)(key.data_mode); + return (uint64_t)(this->data_mode); } }; @@ -109,17 +104,14 @@ struct TextureReadRoutineSpecialisation { (component_count_output == other.component_count_output) && (depth_format_mode == other.depth_format_mode)); } -}; -template<> struct blender::DefaultHash<TextureReadRoutineSpecialisation> { - inline uint64_t operator()(const TextureReadRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; - return (uint64_t)string_hasher(key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + - key.component_count_output + - (key.depth_format_mode << 28))); + blender::DefaultHash<std::string> string_hasher; + return (uint64_t)string_hasher(this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + + this->component_count_output + + (this->depth_format_mode << 28))); } }; @@ -158,21 +150,6 @@ typedef struct MTLSamplerState { const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/}; -} // namespace blender::gpu - -template<> struct blender::DefaultHash<blender::gpu::MTLSamplerState> { - inline uint64_t operator()(const blender::gpu::MTLSamplerState &key) const - { - const DefaultHash<unsigned int> uint_hasher; - uint64_t main_hash = (uint64_t)uint_hasher((unsigned int)(key.state)); - - /* Hash other parameters as needed. */ - return main_hash; - } -}; - -namespace blender::gpu { - class MTLTexture : public Texture { friend class MTLContext; friend class MTLStateManager; @@ -242,7 +219,7 @@ class MTLTexture : public Texture { id<MTLBuffer> vert_buffer_mtl_; int vert_buffer_offset_; - /* Core parameters and subresources. */ + /* Core parameters and sub-resources. */ eGPUTextureUsage gpu_image_usage_flags_; /* Whether the texture's properties or state has changed (e.g. mipmap range), and re-baking of diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index df3efdd12e7..ca19d1f9e4b 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -563,7 +563,7 @@ void gpu::MTLTexture::update_sub( return; } - /* Check Format writeability. */ + /* Check Format write-ability. */ if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) { MTL_LOG_ERROR( "[Error]: Updating texture -- destination MTLPixelFormat '%d' does not support write " @@ -1163,7 +1163,7 @@ void gpu::MTLTexture::mip_range_set(int min, int max) { BLI_assert(min <= max && min >= 0 && max <= mipmaps_); - /* Note: + /* NOTE: * - mip_min_ and mip_max_ are used to Clamp LODs during sampling. * - Given functions like Framebuffer::recursive_downsample modifies the mip range * between each layer, we do not want to be re-baking the texture. @@ -1769,8 +1769,8 @@ void gpu::MTLTexture::ensure_baked() /* CUBE TEXTURES */ case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: { - /* Note: For a cubemap 'Texture::d_' refers to total number of faces, not just array slices - */ + /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces, + * not just array slices. */ BLI_assert(this->w_ > 0 && this->h_ > 0); this->texture_descriptor_ = [[MTLTextureDescriptor alloc] init]; this->texture_descriptor_.pixelFormat = mtl_format; diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index a3288ff4cff..f82138e0d65 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -331,11 +331,21 @@ void object_label(GLenum type, GLuint object, const char *name) char label[64]; SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type)); /* Small convenience for caller. */ - if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER, GL_COMPUTE_SHADER)) { - type = GL_SHADER; - } - if (ELEM(type, GL_UNIFORM_BUFFER)) { - type = GL_BUFFER; + switch (type) { + case GL_FRAGMENT_SHADER: + case GL_GEOMETRY_SHADER: + case GL_VERTEX_SHADER: + case GL_COMPUTE_SHADER: + type = GL_SHADER; + break; + case GL_UNIFORM_BUFFER: + case GL_SHADER_STORAGE_BUFFER: + case GL_ARRAY_BUFFER: + case GL_ELEMENT_ARRAY_BUFFER: + type = GL_BUFFER; + break; + default: + break; } glObjectLabel(type, object, -1, label); } diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 957bc8b24be..5a28b8b7318 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -876,7 +876,7 @@ static char *glsl_patch_default_get() static char *glsl_patch_compute_get() { /** Used for shader patching. Init once. */ - static char patch[512] = "\0"; + static char patch[2048] = "\0"; if (patch[0] != '\0') { return patch; } @@ -889,6 +889,8 @@ static char *glsl_patch_compute_get() /* Array compat. */ STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n"); + STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl); + BLI_assert(slen < sizeof(patch)); return patch; } diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc index 109bb65fcb7..b30674fe5fa 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.cc +++ b/source/blender/gpu/opengl/gl_storage_buffer.cc @@ -144,6 +144,30 @@ void GLStorageBuf::clear(eGPUTextureFormat internal_format, eGPUDataFormat data_ } } +void GLStorageBuf::copy_sub(VertBuf *src_, uint dst_offset, uint src_offset, uint copy_size) +{ + GLVertBuf *src = static_cast<GLVertBuf *>(src_); + GLStorageBuf *dst = this; + + if (dst->ssbo_id_ == 0) { + dst->init(); + } + if (src->vbo_id_ == 0) { + src->bind(); + } + + if (GLContext::direct_state_access_support) { + glCopyNamedBufferSubData(src->vbo_id_, dst->ssbo_id_, src_offset, dst_offset, copy_size); + } + else { + /* This binds the buffer to GL_ARRAY_BUFFER and upload the data if any. */ + src->bind(); + glBindBuffer(GL_COPY_WRITE_BUFFER, dst->ssbo_id_); + glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, src_offset, dst_offset, copy_size); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + } +} + /** \} */ } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_storage_buffer.hh b/source/blender/gpu/opengl/gl_storage_buffer.hh index c808a0bdda1..96052fe0065 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.hh +++ b/source/blender/gpu/opengl/gl_storage_buffer.hh @@ -36,6 +36,7 @@ class GLStorageBuf : public StorageBuf { void bind(int slot) override; void unbind() override; void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override; + void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override; /* Special internal function to bind SSBOs to indirect argument targets. */ void bind_as(GLenum target); diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 2dde8d6c86b..e5b879f1f15 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -363,7 +363,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format) } /** - * Assume Unorm / Float target. Used with #glReadPixels. + * Assume UNORM/Float target. Used with #glReadPixels. */ inline GLenum channel_len_to_gl(int channel_len) { diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 04f60f10d41..cfcf77fe705 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -39,8 +39,8 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertAttr *a = &format->attrs[a_idx]; if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; - stride = a->sz; + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].size) * v_len; + stride = a->size; } else { offset = a->offset; diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a7a0c92431f..6942a220892 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -5,6 +5,8 @@ * \ingroup gpu */ +#include "GPU_texture.h" + #include "gl_context.hh" #include "gl_vertex_buffer.hh" @@ -38,6 +40,7 @@ void GLVertBuf::release_data() } if (vbo_id_ != 0) { + GPU_TEXTURE_FREE_SAFE(buffer_texture_); GLContext::buf_free(vbo_id_); vbo_id_ = 0; memory_usage -= vbo_size_; @@ -51,6 +54,7 @@ void GLVertBuf::duplicate_data(VertBuf *dst_) BLI_assert(GLContext::get() != nullptr); GLVertBuf *src = this; GLVertBuf *dst = static_cast<GLVertBuf *>(dst_); + dst->buffer_texture_ = nullptr; if (src->vbo_id_ != 0) { dst->vbo_size_ = src->size_used_get(); @@ -111,6 +115,16 @@ void GLVertBuf::bind_as_ssbo(uint binding) glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); } +void GLVertBuf::bind_as_texture(uint binding) +{ + bind(); + BLI_assert(vbo_id_ != 0); + if (buffer_texture_ == nullptr) { + buffer_texture_ = GPU_texture_create_from_vertbuf("vertbuf_as_texture", wrap(this)); + } + GPU_texture_bind(buffer_texture_, binding); +} + const void *GLVertBuf::read() const { BLI_assert(is_active()); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index 4c29c17dcf7..e0a21587b60 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -11,18 +11,23 @@ #include "glew-mx.h" +#include "GPU_texture.h" + #include "gpu_vertex_buffer_private.hh" namespace blender { namespace gpu { class GLVertBuf : public VertBuf { - friend class GLTexture; /* For buffer texture. */ - friend class GLShader; /* For transform feedback. */ + friend class GLTexture; /* For buffer texture. */ + friend class GLShader; /* For transform feedback. */ + friend class GLStorageBuf; /* For sub copy. */ private: /** OpenGL buffer handle. Init on first upload. Immutable after that. */ GLuint vbo_id_ = 0; + /** Texture used if the buffer is bound as buffer texture. Init on first use. */ + struct ::GPUTexture *buffer_texture_ = nullptr; /** Defines whether the buffer handle is wrapped by this GLVertBuf, i.e. we do not own it and * should not free it. */ bool is_wrapper_ = false; @@ -46,6 +51,7 @@ class GLVertBuf : public VertBuf { void upload_data() override; void duplicate_data(VertBuf *dst) override; void bind_as_ssbo(uint binding) override; + void bind_as_texture(uint binding) override; private: bool is_active() const; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl index 17240da4050..17240da4050 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_ramp.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_ramp.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index a5c3a990d90..fe89985ae7f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_color_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -90,6 +90,56 @@ void hsv_to_rgb(vec4 hsv, out vec4 outcol) outcol = vec4(rgb, hsv.w); } +void rgb_to_hsl(vec4 rgb, out vec4 outcol) +{ + float cmax, cmin, h, s, l; + + cmax = max(rgb[0], max(rgb[1], rgb[2])); + cmin = min(rgb[0], min(rgb[1], rgb[2])); + l = min(1.0, (cmax + cmin) / 2.0); + + if (cmax == cmin) { + h = s = 0.0; /* achromatic */ + } + else { + float cdelta = cmax - cmin; + s = l > 0.5 ? cdelta / (2.0 - cmax - cmin) : cdelta / (cmax + cmin); + if (cmax == rgb[0]) { + h = (rgb[1] - rgb[2]) / cdelta + (rgb[1] < rgb[2] ? 6.0 : 0.0); + } + else if (cmax == rgb[1]) { + h = (rgb[2] - rgb[0]) / cdelta + 2.0; + } + else { + h = (rgb[0] - rgb[1]) / cdelta + 4.0; + } + } + h /= 6.0; + + outcol = vec4(h, s, l, rgb.w); +} + +void hsl_to_rgb(vec4 hsl, out vec4 outcol) +{ + float nr, ng, nb, chroma, h, s, l; + + h = hsl[0]; + s = hsl[1]; + l = hsl[2]; + + nr = abs(h * 6.0 - 3.0) - 1.0; + ng = 2.0 - abs(h * 6.0 - 2.0); + nb = 2.0 - abs(h * 6.0 - 4.0); + + nr = clamp(nr, 0.0, 1.0); + nb = clamp(nb, 0.0, 1.0); + ng = clamp(ng, 0.0, 1.0); + + chroma = (1.0 - abs(2.0 * l - 1.0)) * s; + + outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w); +} + void color_alpha_clear(vec4 color, out vec4 result) { result = vec4(color.rgb, 1.0); diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl new file mode 100644 index 00000000000..8948ed77557 --- /dev/null +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -0,0 +1,162 @@ +vec4 white_balance(vec4 color, vec4 black_level, vec4 white_level) +{ + vec4 range = max(white_level - black_level, vec4(1e-5f)); + return (color - black_level) / range; +} + +float extrapolate_if_needed(float parameter, float value, float start_slope, float end_slope) +{ + if (parameter < 0.0) { + return value + parameter * start_slope; + } + + if (parameter > 1.0) { + return value + (parameter - 1.0) * end_slope; + } + + return value; +} + +/* Same as extrapolate_if_needed but vectorized. */ +vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3 end_slopes) +{ + vec3 end_or_zero_slopes = mix(vec3(0.0), end_slopes, greaterThan(parameters, vec3(1.0))); + vec3 slopes = mix(end_or_zero_slopes, start_slopes, lessThan(parameters, vec3(0.0))); + parameters = parameters - mix(vec3(0.0), vec3(1.0), greaterThan(parameters, vec3(1.0))); + return values + parameters * slopes; +} + +/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize + * parameters accordingly. */ +#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range) + +void curves_combined_rgb(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + vec4 range_minimums, + vec4 range_dividers, + vec4 start_slopes, + vec4 end_slopes, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa); + + /* Then, evaluate each channel on its curve map. */ + parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb); + result.r = texture(curve_map, vec2(parameters.r, layer)).r; + result.g = texture(curve_map, vec2(parameters.g, layer)).g; + result.b = texture(curve_map, vec2(parameters.b, layer)).b; + + /* Then, extrapolate again if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_combined_only(float factor, + vec4 color, + vec4 black_level, + vec4 white_level, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out vec4 result) +{ + vec4 balanced = white_balance(color, black_level, white_level); + + /* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the + * UI. */ + vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider); + result.r = texture(curve_map, vec2(parameters.x, layer)).a; + result.g = texture(curve_map, vec2(parameters.y, layer)).a; + result.b = texture(curve_map, vec2(parameters.z, layer)).a; + + /* Then, extrapolate if needed. */ + result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope)); + result.a = color.a; + + result = mix(color, result, factor); +} + +void curves_vector(vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + /* Evaluate each component on its curve map. */ + vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers); + result.x = texture(curve_map, vec2(parameters.x, layer)).x; + result.y = texture(curve_map, vec2(parameters.y, layer)).y; + result.z = texture(curve_map, vec2(parameters.z, layer)).z; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameters, result, start_slopes, end_slopes); +} + +void curves_vector_mixed(float factor, + vec3 vector, + sampler1DArray curve_map, + const float layer, + vec3 range_minimums, + vec3 range_dividers, + vec3 start_slopes, + vec3 end_slopes, + out vec3 result) +{ + curves_vector( + vector, curve_map, layer, range_minimums, range_dividers, start_slopes, end_slopes, result); + result = mix(vector, result, factor); +} + +void curves_float(float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + /* Evaluate the normalized value on the first curve map. */ + float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider); + result = texture(curve_map, vec2(parameter, layer)).x; + + /* Then, extrapolate if needed. */ + result = extrapolate_if_needed(parameter, result, start_slope, end_slope); +} + +void curves_float_mixed(float factor, + float value, + sampler1DArray curve_map, + const float layer, + float range_minimum, + float range_divider, + float start_slope, + float end_slope, + out float result) +{ + curves_float( + value, curve_map, layer, range_minimum, range_divider, start_slope, end_slope, result); + result = mix(value, result, factor); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl index 32d61f06a65..32d61f06a65 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_hash.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl index 0948ce2c9fa..5f640f64056 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void math_add(float a, float b, float c, out float result) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index 91a8996939a..124654963fd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -139,75 +139,3 @@ mat3 euler_to_mat3(vec3 euler) mat[2][2] = cy * cx; return mat; } - -void normal_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = normal_object_to_world(vin); -} - -void normal_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = normal_world_to_object(vin); -} - -void direction_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); -} - -void direction_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrix, vin); - vout = transform_direction(ViewMatrix, vout); -} - -void direction_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); -} - -void direction_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrixInverse, vin); - vout = transform_direction(ModelMatrixInverse, vout); -} - -void direction_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ViewMatrix, vin); -} - -void direction_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = transform_direction(ModelMatrixInverse, vin); -} - -void point_transform_object_to_world(vec3 vin, out vec3 vout) -{ - vout = point_object_to_world(vin); -} - -void point_transform_object_to_view(vec3 vin, out vec3 vout) -{ - vout = point_object_to_view(vin); -} - -void point_transform_view_to_world(vec3 vin, out vec3 vout) -{ - vout = point_view_to_world(vin); -} - -void point_transform_view_to_object(vec3 vin, out vec3 vout) -{ - vout = point_view_to_object(vin); -} - -void point_transform_world_to_view(vec3 vin, out vec3 vout) -{ - vout = point_world_to_view(vin); -} - -void point_transform_world_to_object(vec3 vin, out vec3 vout) -{ - vout = point_world_to_object(vin); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index 157a6a27c15..f9652f1150b 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl deleted file mode 100644 index e0e899cfb35..00000000000 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ /dev/null @@ -1,93 +0,0 @@ - -#define INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) \ - { \ - vec2 v[4]; \ - int primOffset = (gl_PrimitiveID + PrimitiveIdBase) * 4; \ - for (int i = 0; i < 4; i++) { \ - int index = (primOffset + i) * osd_fvar_count + fvarOffset; \ - v[i] = vec2(texelFetch(FVarDataBuffer, index).s, texelFetch(FVarDataBuffer, index + 1).s); \ - } \ - result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); \ - } - -#define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ - { \ - vec2 tmp; \ - INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \ - result = vec3(tmp, 0); \ - } - -out block -{ - VertexData v; -} -outpt; - -void set_mtface_vertex_attrs(vec2 st); - -void emit_flat(int index, vec3 normal) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void emit_smooth(int index) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = inpt[index].v.normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void main() -{ - gl_PrimitiveID = gl_PrimitiveIDIn; - - if (osd_flat_shading) { - vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz; - vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz; - vec3 flat_normal = normalize(cross(B, A)); - emit_flat(0, flat_normal); - emit_flat(1, flat_normal); - emit_flat(3, flat_normal); - emit_flat(2, flat_normal); - } - else { - emit_smooth(0); - emit_smooth(1); - emit_smooth(3); - emit_smooth(2); - } - EndPrimitive(); -} - -void set_mtface_vertex_attrs(vec2 st) -{ diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh new file mode 100644 index 00000000000..94cf58933af --- /dev/null +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_interface_info.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(gpu_shader_3D_image) + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(1, Type::VEC2, "texCoord") + .vertex_out(smooth_tex_coord_interp_iface) + .fragment_out(0, Type::VEC4, "fragColor") + .push_constant(Type::MAT4, "ModelViewProjectionMatrix") + .sampler(0, ImageType::FLOAT_2D, "image") + .vertex_source("gpu_shader_3D_image_vert.glsl") + .fragment_source("gpu_shader_image_frag.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh index 369fe3ac293..ae7edeb16e2 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_uniform_color_info.hh @@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_clipped_uniform_color) .fragment_out(0, Type::VEC4, "fragColor") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .push_constant(Type::VEC4, "color") - /* TODO(fclem): Put thoses two to one UBO. */ + /* TODO(@fclem): Put those two to one UBO. */ .push_constant(Type::MAT4, "ModelMatrix") .push_constant(Type::VEC4, "ClipPlane") .vertex_source("gpu_shader_3D_clipped_uniform_color_vert.glsl") diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl new file mode 100644 index 00000000000..e68d0d98484 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_color.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void combine_color_rgb(float r, float g, float b, out vec4 col) +{ + col = vec4(r, g, b, 1.0); +} + +void combine_color_hsv(float h, float s, float v, out vec4 col) +{ + hsv_to_rgb(vec4(h, s, v, 1.0), col); +} + +void combine_color_hsl(float h, float s, float l, out vec4 col) +{ + hsl_to_rgb(vec4(h, s, l, 1.0), col); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl index e8f444080b9..4d9e16afe66 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void combine_hsv(float h, float s, float v, out vec4 col) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl deleted file mode 100644 index 514409f7fdf..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl +++ /dev/null @@ -1,33 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_float_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curve_float(float fac, - float vec, - sampler1DArray curvemap, - float layer, - float range, - vec4 ext, - out float outvec) -{ - float xyz_min = ext.x; - vec = RANGE_RESCALE(vec, xyz_min, range); - - outvec = texture(curvemap, vec2(vec, layer)).x; - - outvec = curve_float_extrapolate(vec, outvec, ext); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index 7f502f74c0c..6d52e97cca1 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* The fractal_noise functions are all exactly the same except for the input type. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl index 29fb09ceebd..64681cd795a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_gamma(vec4 col, float gamma, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 2d5114c3bad..61458b05c86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_hair_info(float hair_length, out float is_strand, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl index 30b808508e9..5223828e176 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl index 119ee3c0eaa..a81e6d36a55 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) float smootherstep(float edge0, float edge1, float x) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl index 312c57231c5..b59257506c9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void mapping_mat4( vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl index c84f34a834c..881e38ea11a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* clang-format off */ #define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl index ad3d4737193..251103ae57c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) void node_point_info(out vec3 position, out float radius, out float random) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl deleted file mode 100644 index 054fdddf7c3..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_rgb_curves.glsl +++ /dev/null @@ -1,73 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_rgb(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_r, - vec4 ext_g, - vec4 ext_b, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - samp.r = curve_extrapolate(co.x, samp.r, ext_a); - samp.g = curve_extrapolate(co.y, samp.g, ext_a); - samp.b = curve_extrapolate(co.z, samp.b, ext_a); - - vec3 rgb_min = vec3(ext_r.x, ext_g.x, ext_b.x); - co.xyz = RANGE_RESCALE(samp.rgb, rgb_min, range.rgb); - - samp.r = texture(curvemap, co.xw).r; - samp.g = texture(curvemap, co.yw).g; - samp.b = texture(curvemap, co.zw).b; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_r); - outcol.g = curve_extrapolate(co.y, samp.g, ext_g); - outcol.b = curve_extrapolate(co.z, samp.b, ext_b); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} - -void curves_rgb_opti(float fac, - vec4 col, - sampler1DArray curvemap, - float layer, - vec4 range, - vec4 ext_a, - out vec4 outcol) -{ - vec4 co = vec4(RANGE_RESCALE(col.rgb, ext_a.x, range.a), layer); - vec3 samp; - samp.r = texture(curvemap, co.xw).a; - samp.g = texture(curvemap, co.yw).a; - samp.b = texture(curvemap, co.zw).a; - - outcol.r = curve_extrapolate(co.x, samp.r, ext_a); - outcol.g = curve_extrapolate(co.y, samp.g, ext_a); - outcol.b = curve_extrapolate(co.z, samp.b, ext_a); - outcol.a = col.a; - - outcol = mix(col, outcol, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl new file mode 100644 index 00000000000..2dd51029cef --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_color.glsl @@ -0,0 +1,28 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void separate_color_rgb(vec4 col, out float r, out float g, out float b) +{ + r = col.r; + g = col.g; + b = col.b; +} + +void separate_color_hsv(vec4 col, out float r, out float g, out float b) +{ + vec4 hsv; + + rgb_to_hsv(col, hsv); + r = hsv[0]; + g = hsv[1]; + b = hsv[2]; +} + +void separate_color_hsl(vec4 col, out float r, out float g, out float b) +{ + vec4 hsl; + + rgb_to_hsl(col, hsl); + r = hsl[0]; + g = hsl[1]; + b = hsl[2]; +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl index 180e0fd1940..8e475ec39a7 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) void separate_hsv(vec4 col, out float h, out float s, out float v) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl index edc2fa32177..8d9773913ff 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec2 calc_brick_texture(vec3 p, float mortar_size, diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index da131978f72..cf7d6ae18e6 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl index 1552a2facc3..961fe23e67e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) /* 1D Musgrave fBm diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index c90b2211dcf..3df6f7b29fb 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl index dd12b602edf..0fb8ef15f5f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl @@ -1,5 +1,5 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) /* * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl index eed98232d0b..c24a9417219 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl) #pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl) diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl index 030b58f0736..c5081372aa4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) /* White Noise */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl new file mode 100644 index 00000000000..87048e5c5d6 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_transform_utils.glsl @@ -0,0 +1,71 @@ +void normal_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = normal_object_to_world(vin); +} + +void normal_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = normal_world_to_object(vin); +} + +void direction_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); +} + +void direction_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrix, vin); + vout = transform_direction(ViewMatrix, vout); +} + +void direction_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); +} + +void direction_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrixInverse, vin); + vout = transform_direction(ModelMatrixInverse, vout); +} + +void direction_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ViewMatrix, vin); +} + +void direction_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = transform_direction(ModelMatrixInverse, vin); +} + +void point_transform_object_to_world(vec3 vin, out vec3 vout) +{ + vout = point_object_to_world(vin); +} + +void point_transform_object_to_view(vec3 vin, out vec3 vout) +{ + vout = point_object_to_view(vin); +} + +void point_transform_view_to_world(vec3 vin, out vec3 vout) +{ + vout = point_view_to_world(vin); +} + +void point_transform_view_to_object(vec3 vin, out vec3 vout) +{ + vout = point_view_to_object(vin); +} + +void point_transform_world_to_view(vec3 vin, out vec3 vout) +{ + vout = point_world_to_view(vin); +} + +void point_transform_world_to_object(vec3 vin, out vec3 vout) +{ + vout = point_world_to_object(vin); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl deleted file mode 100644 index f6dec1b24e2..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_curves.glsl +++ /dev/null @@ -1,41 +0,0 @@ -/* ext is vec4(in_x, in_dy, out_x, out_dy). */ -float curve_vec_extrapolate(float x, float y, vec4 ext) -{ - if (x < 0.0) { - return y + x * ext.y; - } - else if (x > 1.0) { - return y + (x - 1.0) * ext.w; - } - else { - return y; - } -} - -#define RANGE_RESCALE(x, min, range) ((x - min) * range) - -void curves_vec(float fac, - vec3 vec, - sampler1DArray curvemap, - float layer, - vec3 range, - vec4 ext_x, - vec4 ext_y, - vec4 ext_z, - out vec3 outvec) -{ - vec4 co = vec4(vec, layer); - - vec3 xyz_min = vec3(ext_x.x, ext_y.x, ext_z.x); - co.xyz = RANGE_RESCALE(co.xyz, xyz_min, range); - - outvec.x = texture(curvemap, co.xw).x; - outvec.y = texture(curvemap, co.yw).y; - outvec.z = texture(curvemap, co.zw).z; - - outvec.x = curve_vec_extrapolate(co.x, outvec.r, ext_x); - outvec.y = curve_vec_extrapolate(co.y, outvec.g, ext_y); - outvec.z = curve_vec_extrapolate(co.z, outvec.b, ext_z); - - outvec = mix(vec, outvec, fac); -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 8f6bf17f195..018784c42a5 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) { diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl index ff0fb1c0418..8f7bd26ca18 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl @@ -1,4 +1,4 @@ -#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) vec3 rotate_around_axis(vec3 p, vec3 axis, float angle) { diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index 6ef8a032a73..5dc70a8bf0f 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -35,6 +35,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index e46326467cc..1309e3810be 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -178,13 +178,13 @@ if(WITH_IMAGE_WEBP) list(APPEND SRC intern/webp.c ) - list(APPEND INC_SYS - ${WEBP_INCLUDE_DIRS} - ) + list(APPEND INC_SYS + ${WEBP_INCLUDE_DIRS} + ) list(APPEND LIB ${WEBP_LIBRARIES} ) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() list(APPEND INC diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 0390df06052..20c414bb1ad 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -109,6 +109,14 @@ struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[I /** * + * \attention Defined in readimage.c + */ +struct ImBuf *IMB_thumb_load_image(const char *filepath, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]); + +/** + * * \attention Defined in allocimbuf.c */ void IMB_freeImBuf(struct ImBuf *ibuf); @@ -522,7 +530,6 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); -bool IMB_prepare_write_ImBuf(bool isfloat, struct ImBuf *ibuf); /** * @@ -546,12 +553,6 @@ bool IMB_isanim(const char *filepath); int imb_get_anim_type(const char *filepath); /** - * - * \attention Defined in util.c - */ -bool IMB_isfloat(const struct ImBuf *ibuf); - -/** * Test if color-space conversions of pixels in buffer need to take into account alpha. */ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf); @@ -935,8 +936,7 @@ const char *IMB_ffmpeg_last_error(void); struct GPUTexture *IMB_create_gpu_texture(const char *name, struct ImBuf *ibuf, bool use_high_bitdepth, - bool use_premult, - bool limit_gl_texture_size); + bool use_premult); /** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 934163846e4..16cf0e2125e 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -47,7 +47,7 @@ typedef struct DDSData { * Also; add new variables to the end to save pain! */ -/* Warning: Keep explicit value assignments here, +/* WARNING: Keep explicit value assignments here, * this file is included in areas where not all format defines are set * (e.g. intern/dds only get WITH_DDS, even if TIFF, HDR etc are also defined). * See T46524. */ diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index 623a3b2b5f4..b55a6f653b8 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -50,7 +50,7 @@ typedef enum ThumbSource { /** * Create thumbnail for file and returns new imbuf for thumbnail. */ -struct ImBuf *IMB_thumb_create(const char *path, +struct ImBuf *IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, struct ImBuf *img); @@ -58,17 +58,17 @@ struct ImBuf *IMB_thumb_create(const char *path, /** * Read thumbnail for file and returns new imbuf for thumbnail. */ -struct ImBuf *IMB_thumb_read(const char *path, ThumbSize size); +struct ImBuf *IMB_thumb_read(const char *filepath, ThumbSize size); /** * Delete all thumbs for the file. */ -void IMB_thumb_delete(const char *path, ThumbSize size); +void IMB_thumb_delete(const char *filepath, ThumbSize size); /** * Create the thumb if necessary and manage failed and old thumbs. */ -struct ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source); +struct ImBuf *IMB_thumb_manage(const char *filepath, ThumbSize size, ThumbSource source); /** * Create the necessary directories to store the thumbnails. @@ -85,7 +85,7 @@ struct ImBuf *IMB_thumb_load_blend(const char *blen_path, /** * Special function for previewing fonts. */ -struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y); +struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y); bool IMB_thumb_load_font_get_hash(char *r_hash); void IMB_thumb_clear_translations(void); void IMB_thumb_ensure_translations(void); diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h index 31f8b3a9505..67d1aefeacb 100644 --- a/source/blender/imbuf/intern/IMB_filetype.h +++ b/source/blender/imbuf/intern/IMB_filetype.h @@ -36,6 +36,17 @@ typedef struct ImFileType { char colorspace[IM_MAX_SPACE]); /** Load an image from a file. */ struct ImBuf *(*load_filepath)(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]); + /** + * Load/Create a thumbnail image from a filepath. `max_thumb_size` is maximum size of either + * dimension, so can return less on either or both. Should, if possible and performant, return + * dimensions of the full-size image in r_width & r_height. + */ + struct ImBuf *(*load_filepath_thumbnail)(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** Save to a file (or memory if #IB_mem is set in `flags` and the format supports it). */ bool (*save)(struct ImBuf *ibuf, const char *filepath, int flags); void (*load_tile)(struct ImBuf *ibuf, @@ -143,6 +154,12 @@ struct ImBuf *imb_load_jpeg(const unsigned char *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE]); +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height); /** \} */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 096089d4c41..0052ce19aa1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -423,7 +423,7 @@ static int startavi(struct anim *anim) anim->cur_position = 0; # if 0 - printf("x:%d y:%d size:%d interl:%d dur:%d\n", + printf("x:%d y:%d size:%d interlace:%d dur:%d\n", anim->x, anim->y, anim->framesize, diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c index d1cd30cfe84..6448d6cd76a 100644 --- a/source/blender/imbuf/intern/cineon/cineon_dpx.c +++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c @@ -69,7 +69,7 @@ static struct ImBuf *imb_load_dpx_cineon(const unsigned char *mem, return ibuf; } -static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon, int flags) +static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon, int flags) { LogImageFile *logImage; float *fbuf; @@ -86,7 +86,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon depth = (ibuf->planes + 7) >> 3; if (depth > 4 || depth < 3) { - printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filename); + printf("DPX/Cineon: unsupported depth: %d for file: '%s'\n", depth, filepath); return 0; } @@ -103,7 +103,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filename, int use_cineon bitspersample = 8; } - logImage = logImageCreate(filename, + logImage = logImageCreate(filepath, use_cineon, ibuf->x, ibuf->y, diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c index 3bdfcb60292..8312476bda0 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.c +++ b/source/blender/imbuf/intern/cineon/cineonlib.c @@ -35,7 +35,7 @@ void cineonSetVerbose(int verbosity) static void fillCineonMainHeader(LogImageFile *cineon, CineonMainHeader *header, - const char *filename, + const char *filepath, const char *creator) { time_t fileClock; @@ -57,7 +57,7 @@ static void fillCineonMainHeader(LogImageFile *cineon, getRowLength(cineon->width, cineon->element[0]), cineon->isMSB); strcpy(header->fileHeader.version, "v4.5"); - strncpy(header->fileHeader.file_name, filename, 99); + strncpy(header->fileHeader.file_name, filepath, 99); header->fileHeader.file_name[99] = 0; fileClock = time(NULL); fileTime = localtime(&fileClock); @@ -126,7 +126,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t { CineonMainHeader header; LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; unsigned int dataOffset; @@ -144,11 +144,11 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t cineon->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - cineon->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + cineon->file = BLI_fopen(filepath, "rb"); if (cineon->file == NULL) { if (verbose) { - printf("Cineon: Failed to open file \"%s\".\n", filename); + printf("Cineon: Failed to open file \"%s\".\n", filepath); } logImageClose(cineon); return NULL; @@ -350,7 +350,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t } LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator) + const char *filepath, int width, int height, int bitsPerSample, const char *creator) { CineonMainHeader header; const char *shortFilename = NULL; @@ -393,18 +393,18 @@ LogImageFile *cineonCreate( cineon->referenceBlack = 95.0f; cineon->gamma = 1.7f; - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - cineon->file = BLI_fopen(filename, "wb"); + cineon->file = BLI_fopen(filepath, "wb"); if (cineon->file == NULL) { if (verbose) { - printf("cineon: Couldn't open file %s\n", filename); + printf("cineon: Couldn't open file %s\n", filepath); } logImageClose(cineon); return NULL; diff --git a/source/blender/imbuf/intern/cineon/cineonlib.h b/source/blender/imbuf/intern/cineon/cineonlib.h index 37b27d19539..13d40461728 100644 --- a/source/blender/imbuf/intern/cineon/cineonlib.h +++ b/source/blender/imbuf/intern/cineon/cineonlib.h @@ -114,7 +114,7 @@ typedef struct { void cineonSetVerbose(int); LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); LogImageFile *cineonCreate( - const char *filename, int width, int height, int bitsPerSample, const char *creator); + const char *filepath, int width, int height, int bitsPerSample, const char *creator); #ifdef __cplusplus } diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c index 2d28a477c8a..28c19116361 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.c +++ b/source/blender/imbuf/intern/cineon/dpxlib.c @@ -124,7 +124,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf { DpxMainHeader header; LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__); - const char *filename = (const char *)byteStuff; + const char *filepath = (const char *)byteStuff; int i; if (dpx == NULL) { @@ -141,11 +141,11 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf dpx->file = NULL; if (fromMemory == 0) { - /* byteStuff is then the filename */ - dpx->file = BLI_fopen(filename, "rb"); + /* byteStuff is then the filepath */ + dpx->file = BLI_fopen(filepath, "rb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Failed to open file \"%s\".\n", filename); + printf("DPX: Failed to open file \"%s\".\n", filepath); } logImageClose(dpx); return NULL; @@ -406,7 +406,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf return dpx; } -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, @@ -502,19 +502,19 @@ LogImageFile *dpxCreate(const char *filename, dpx->gamma = 1.7f; } - shortFilename = strrchr(filename, '/'); + shortFilename = strrchr(filepath, PATHSEP_CHAR); if (shortFilename == NULL) { - shortFilename = filename; + shortFilename = filepath; } else { shortFilename++; } - dpx->file = BLI_fopen(filename, "wb"); + dpx->file = BLI_fopen(filepath, "wb"); if (dpx->file == NULL) { if (verbose) { - printf("DPX: Couldn't open file %s\n", filename); + printf("DPX: Couldn't open file %s\n", filepath); } logImageClose(dpx); return NULL; diff --git a/source/blender/imbuf/intern/cineon/dpxlib.h b/source/blender/imbuf/intern/cineon/dpxlib.h index d8ed5dc6f67..aac424d52d6 100644 --- a/source/blender/imbuf/intern/cineon/dpxlib.h +++ b/source/blender/imbuf/intern/cineon/dpxlib.h @@ -133,7 +133,7 @@ typedef struct { void dpxSetVerbose(int verbosity); LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize); -LogImageFile *dpxCreate(const char *filename, +LogImageFile *dpxCreate(const char *filepath, int width, int height, int bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.c b/source/blender/imbuf/intern/cineon/logImageCore.c index 36e12e07316..e693aa6f891 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.c +++ b/source/blender/imbuf/intern/cineon/logImageCore.c @@ -101,10 +101,10 @@ int logImageIsCineon(const void *buffer, const unsigned int size) return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1)); } -LogImageFile *logImageOpenFromFile(const char *filename, int cineon) +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon) { unsigned int magicNum; - FILE *f = BLI_fopen(filename, "rb"); + FILE *f = BLI_fopen(filepath, "rb"); (void)cineon; @@ -120,10 +120,10 @@ LogImageFile *logImageOpenFromFile(const char *filename, int cineon) fclose(f); if (logImageIsDpx(&magicNum, sizeof(magicNum))) { - return dpxOpen((const unsigned char *)filename, 0, 0); + return dpxOpen((const unsigned char *)filepath, 0, 0); } if (logImageIsCineon(&magicNum, sizeof(magicNum))) { - return cineonOpen((const unsigned char *)filename, 0, 0); + return cineonOpen((const unsigned char *)filepath, 0, 0); } return NULL; @@ -141,7 +141,7 @@ LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int s return NULL; } -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, @@ -155,10 +155,10 @@ LogImageFile *logImageCreate(const char *filename, { /* referenceWhite, referenceBlack and gamma values are only supported for DPX file */ if (cineon) { - return cineonCreate(filename, width, height, bitsPerSample, creator); + return cineonCreate(filepath, width, height, bitsPerSample, creator); } - return dpxCreate(filename, + return dpxCreate(filepath, width, height, bitsPerSample, diff --git a/source/blender/imbuf/intern/cineon/logImageCore.h b/source/blender/imbuf/intern/cineon/logImageCore.h index 6875dba3f87..35540497828 100644 --- a/source/blender/imbuf/intern/cineon/logImageCore.h +++ b/source/blender/imbuf/intern/cineon/logImageCore.h @@ -19,6 +19,12 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" +#ifdef _WIN32 +# define PATHSEP_CHAR '\\' +#else +# define PATHSEP_CHAR '/' +#endif + #ifdef __cplusplus extern "C" { #endif @@ -169,9 +175,9 @@ void logImageSetVerbose(int verbosity); int logImageIsDpx(const void *buffer, unsigned int size); int logImageIsCineon(const void *buffer, unsigned int size); LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size); -LogImageFile *logImageOpenFromFile(const char *filename, int cineon); +LogImageFile *logImageOpenFromFile(const char *filepath, int cineon); void logImageGetSize(LogImageFile *logImage, int *width, int *height, int *depth); -LogImageFile *logImageCreate(const char *filename, +LogImageFile *logImageCreate(const char *filepath, int cineon, int width, int height, diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 670394e1a1a..d4c9e78a299 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -2451,68 +2451,77 @@ void IMB_colormanagement_imbuf_make_display_space( colormanagement_imbuf_make_display_space(ibuf, view_settings, display_settings, false); } +static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool allocate_result) +{ + if (colormanaged_ibuf != ibuf) { + /* Is already an editable copy. */ + return colormanaged_ibuf; + } + + if (allocate_result) { + /* Copy full image buffer. */ + colormanaged_ibuf = IMB_dupImBuf(ibuf); + IMB_metadata_copy(colormanaged_ibuf, ibuf); + return colormanaged_ibuf; + } + else { + /* Render pipeline is constructing image buffer itself, + * but it's re-using byte and float buffers from render result make copy of this buffers + * here sine this buffers would be transformed to other color space here. */ + if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { + ibuf->rect = MEM_dupallocN(ibuf->rect); + ibuf->mall |= IB_rect; + } + + if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { + ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); + ibuf->mall |= IB_rectfloat; + } + + return ibuf; + } +} + ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ImageFormatData *image_format) { ImBuf *colormanaged_ibuf = ibuf; - const bool is_movie = BKE_imtype_is_movie(image_format->imtype); - const bool requires_linear_float = BKE_imtype_requires_linear_float(image_format->imtype); - const bool do_alpha_under = image_format->planes != R_IMF_PLANES_RGBA; + /* Update byte buffer if exists but invalid. */ if (ibuf->rect_float && ibuf->rect && (ibuf->userflags & (IB_DISPLAY_BUFFER_INVALID | IB_RECT_INVALID)) != 0) { IMB_rect_from_float(ibuf); ibuf->userflags &= ~(IB_RECT_INVALID | IB_DISPLAY_BUFFER_INVALID); } - const bool do_colormanagement_display = save_as_render && (is_movie || !requires_linear_float); - const bool do_colormanagement_linear = save_as_render && requires_linear_float && - image_format->linear_colorspace_settings.name[0] && - !IMB_colormanagement_space_name_is_scene_linear( - image_format->linear_colorspace_settings.name); - - if (do_colormanagement_display || do_colormanagement_linear || do_alpha_under) { - if (allocate_result) { - colormanaged_ibuf = IMB_dupImBuf(ibuf); - } - else { - /* Render pipeline is constructing image buffer itself, - * but it's re-using byte and float buffers from render result make copy of this buffers - * here sine this buffers would be transformed to other color space here. - */ + /* Detect if we are writing to a file format that needs a linear float buffer. */ + const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype); - if (ibuf->rect && (ibuf->mall & IB_rect) == 0) { - ibuf->rect = MEM_dupallocN(ibuf->rect); - ibuf->mall |= IB_rect; - } + /* Detect if we are writing output a byte buffer, which we would need to create + * with color management conversions applied. This may be for either applying the + * display transform for renders, or a user specified color space for the file. */ + const bool byte_output = BKE_image_format_is_byte(image_format); - if (ibuf->rect_float && (ibuf->mall & IB_rectfloat) == 0) { - ibuf->rect_float = MEM_dupallocN(ibuf->rect_float); - ibuf->mall |= IB_rectfloat; - } - } - } + BLI_assert(!(byte_output && linear_float_output)); - /* If we're saving from RGBA to RGB buffer then it's not - * so much useful to just ignore alpha -- it leads to bad - * artifacts especially when saving byte images. + /* If we're saving from RGBA to RGB buffer then it's not so much useful to just ignore alpha -- + * it leads to bad artifacts especially when saving byte images. * - * What we do here is we're overlaying our image on top of - * background color (which is currently black). + * What we do here is we're overlaying our image on top of background color (which is currently + * black). This is quite much the same as what Gimp does and it seems to be what artists expects + * from saving. * - * This is quite much the same as what Gimp does and it - * seems to be what artists expects from saving. - * - * Do a conversion here, so image format writers could - * happily assume all the alpha tricks were made already. - * helps keep things locally here, not spreading it to - * all possible image writers we've got. + * Do a conversion here, so image format writers could happily assume all the alpha tricks were + * made already. helps keep things locally here, not spreading it to all possible image writers + * we've got. */ - if (do_alpha_under) { + if (image_format->planes != R_IMF_PLANES_RGBA) { float color[3] = {0, 0, 0}; + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + if (colormanaged_ibuf->rect_float && colormanaged_ibuf->channels == 4) { IMB_alpha_under_color_float( colormanaged_ibuf->rect_float, colormanaged_ibuf->x, colormanaged_ibuf->y, color); @@ -2526,67 +2535,93 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, } } - if (do_colormanagement_display) { - /* Color management with display and view transform. */ - bool make_byte = false; + if (save_as_render && !linear_float_output) { + /* Render output: perform conversion to display space using view transform. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); - /* for proper check whether byte buffer is required by a format or not - * should be pretty safe since this image buffer is supposed to be used for - * saving only and ftype would be overwritten a bit later by BKE_imbuf_write - */ - colormanaged_ibuf->ftype = BKE_imtype_to_ftype(image_format->imtype, - &colormanaged_ibuf->foptions); - - /* if file format isn't able to handle float buffer itself, - * we need to allocate byte buffer and store color managed - * image there - */ - const ImFileType *type = IMB_file_type_from_ibuf(colormanaged_ibuf); - if (type != NULL) { - if ((type->save != NULL) && (type->flag & IM_FTYPE_FLOAT) == 0) { - make_byte = true; - } - } - - /* perform color space conversions */ colormanagement_imbuf_make_display_space(colormanaged_ibuf, &image_format->view_settings, &image_format->display_settings, - make_byte); + byte_output); if (colormanaged_ibuf->rect_float) { - /* float buffer isn't linear anymore, + /* Float buffer isn't linear anymore, * image format write callback should check for this flag and assume - * no space conversion should happen if ibuf->float_colorspace != NULL - */ + * no space conversion should happen if ibuf->float_colorspace != NULL. */ colormanaged_ibuf->float_colorspace = display_transform_get_colorspace( &image_format->view_settings, &image_format->display_settings); + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanaged_ibuf->float_colorspace; + } } } - else if (do_colormanagement_linear) { - /* Color management transform to another linear color space. */ - if (!colormanaged_ibuf->rect_float) { - IMB_float_from_rect(colormanaged_ibuf); - imb_freerectImBuf(colormanaged_ibuf); + else { + /* Linear render or regular file output: conversion between two color spaces. */ + + /* Detect which color space we need to convert between. */ + const char *from_colorspace = (ibuf->rect_float && !(byte_output && ibuf->rect)) ? + /* From float buffer. */ + (ibuf->float_colorspace) ? ibuf->float_colorspace->name : + global_role_scene_linear : + /* From byte buffer. */ + (ibuf->rect_colorspace) ? ibuf->rect_colorspace->name : + global_role_default_byte; + + const char *to_colorspace = image_format->linear_colorspace_settings.name; + + /* TODO: can we check with OCIO if color spaces are the same but have different names? */ + if (to_colorspace[0] == '\0' || STREQ(from_colorspace, to_colorspace)) { + /* No conversion needed, but may still need to allocate byte buffer for output. */ + if (byte_output && !ibuf->rect) { + ibuf->rect_colorspace = ibuf->float_colorspace; + IMB_rect_from_float(ibuf); + } } + else { + /* Color space conversion needed. */ + colormanaged_ibuf = imbuf_ensure_editable(ibuf, colormanaged_ibuf, allocate_result); + + if (byte_output) { + colormanaged_ibuf->rect_colorspace = colormanage_colorspace_get_named(to_colorspace); + + if (colormanaged_ibuf->rect) { + /* Byte to byte. */ + IMB_colormanagement_transform_byte_threaded((unsigned char *)colormanaged_ibuf->rect, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace); + } + else { + /* Float to byte. */ + IMB_rect_from_float(colormanaged_ibuf); + } + } + else { + if (!colormanaged_ibuf->rect_float) { + /* Byte to float. */ + IMB_float_from_rect(colormanaged_ibuf); + imb_freerectImBuf(colormanaged_ibuf); - if (colormanaged_ibuf->rect_float) { - const char *from_colorspace = (ibuf->float_colorspace) ? ibuf->float_colorspace->name : - global_role_scene_linear; - const char *to_colorspace = image_format->linear_colorspace_settings.name; - - IMB_colormanagement_transform(colormanaged_ibuf->rect_float, - colormanaged_ibuf->x, - colormanaged_ibuf->y, - colormanaged_ibuf->channels, - from_colorspace, - to_colorspace, - false); - } - } + /* This conversion always goes to scene linear. */ + from_colorspace = global_role_scene_linear; + } - if (colormanaged_ibuf != ibuf) { - IMB_metadata_copy(colormanaged_ibuf, ibuf); + if (colormanaged_ibuf->rect_float) { + /* Float to float. */ + IMB_colormanagement_transform(colormanaged_ibuf->rect_float, + colormanaged_ibuf->x, + colormanaged_ibuf->y, + colormanaged_ibuf->channels, + from_colorspace, + to_colorspace, + false); + + colormanaged_ibuf->float_colorspace = colormanage_colorspace_get_named(to_colorspace); + } + } + } } return colormanaged_ibuf; diff --git a/source/blender/imbuf/intern/dds/Color.h b/source/blender/imbuf/intern/dds/Color.h index 3918ef31052..5b00333ad77 100644 --- a/source/blender/imbuf/intern/dds/Color.h +++ b/source/blender/imbuf/intern/dds/Color.h @@ -21,9 +21,8 @@ class Color32 { Color32() { } - Color32(const Color32 &c) : u(c.u) - { - } + Color32(const Color32 &) = default; + Color32(unsigned char R, unsigned char G, unsigned char B) { setRGBA(R, G, B, 0xFF); diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp index 34f3654aa3f..566891dac8b 100644 --- a/source/blender/imbuf/intern/dds/Stream.cpp +++ b/source/blender/imbuf/intern/dds/Stream.cpp @@ -45,7 +45,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i) mem.set_failed(msg_error_seek); return 0; } - memcpy(&i, mem.mem + mem.pos, 8); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 8); /* TODO: make sure little endian. */ mem.pos += 8; return 8; } @@ -56,7 +56,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 4); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 4); /* TODO: make sure little endian. */ mem.pos += 4; return 4; } @@ -67,7 +67,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i) mem.set_failed(msg_error_read); return 0; } - memcpy(&i, mem.mem + mem.pos, 2); /* @@ todo: make sure little endian */ + memcpy(&i, mem.mem + mem.pos, 2); /* TODO: make sure little endian. */ mem.pos += 2; return 2; } diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c index 548bc9e120c..92fa980cd7f 100644 --- a/source/blender/imbuf/intern/filetype.c +++ b/source/blender/imbuf/intern/filetype.c @@ -33,6 +33,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jpeg, .load = imb_load_jpeg, .load_filepath = NULL, + .load_filepath_thumbnail = imb_thumbnail_jpeg, .save = imb_savejpeg, .load_tile = NULL, .flag = 0, @@ -45,6 +46,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_png, .load = imb_loadpng, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savepng, .load_tile = NULL, .flag = 0, @@ -57,6 +59,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_bmp, .load = imb_bmp_decode, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savebmp, .load_tile = NULL, .flag = 0, @@ -69,6 +72,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_targa, .load = imb_loadtarga, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetarga, .load_tile = NULL, .flag = 0, @@ -81,6 +85,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_iris, .load = imb_loadiris, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_saveiris, .load_tile = NULL, .flag = 0, @@ -94,6 +99,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dpx, .load = imb_load_dpx, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_dpx, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -106,6 +112,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_cineon, .load = imb_load_cineon, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_cineon, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -120,6 +127,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_tiff, .load = imb_loadtiff, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savetiff, .load_tile = imb_loadtiletiff, .flag = 0, @@ -134,6 +142,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_hdr, .load = imb_loadhdr, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savehdr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -148,6 +157,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_openexr, .load = imb_load_openexr, .load_filepath = NULL, + .load_filepath_thumbnail = imb_load_filepath_thumbnail_openexr, .save = imb_save_openexr, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -162,6 +172,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_jp2, .load = imb_load_jp2, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_save_jp2, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -176,6 +187,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_dds, .load = imb_load_dds, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = 0, @@ -190,6 +202,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_photoshop, .load = NULL, .load_filepath = imb_load_photoshop, + .load_filepath_thumbnail = NULL, .save = NULL, .load_tile = NULL, .flag = IM_FTYPE_FLOAT, @@ -204,6 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = { .is_a = imb_is_a_webp, .load = imb_loadwebp, .load_filepath = NULL, + .load_filepath_thumbnail = NULL, .save = imb_savewebp, .load_tile = NULL, .flag = 0, @@ -211,7 +225,7 @@ const ImFileType IMB_FILE_TYPES[] = { .default_save_role = COLOR_ROLE_DEFAULT_BYTE, }, #endif - {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, }; const ImFileType *IMB_FILE_TYPES_LAST = &IMB_FILE_TYPES[ARRAY_SIZE(IMB_FILE_TYPES) - 1]; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 1cc91d25d2a..cbc5d984755 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -376,10 +376,10 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc) static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len) { if (!anim->index_dir[0]) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, index_dir, fname, index_dir_len, sizeof(fname)); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, index_dir, filename, index_dir_len, sizeof(filename)); BLI_path_append(index_dir, index_dir_len, "BL_proxy"); - BLI_path_append(index_dir, index_dir_len, fname); + BLI_path_append(index_dir, index_dir_len, filename); } else { BLI_strncpy(index_dir, anim->index_dir, index_dir_len); @@ -388,14 +388,14 @@ static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_l void IMB_anim_get_fname(struct anim *anim, char *file, int size) { - char fname[FILE_MAXFILE]; - BLI_split_dirfile(anim->name, file, fname, size, sizeof(fname)); - BLI_strncpy(file, fname, size); + char filename[FILE_MAXFILE]; + BLI_split_dirfile(anim->name, file, filename, size, sizeof(filename)); + BLI_strncpy(file, filename, size); } -static bool get_proxy_filename(struct anim *anim, +static bool get_proxy_filepath(struct anim *anim, IMB_Proxy_Size preview_size, - char *fname, + char *filepath, bool temp) { char index_dir[FILE_MAXDIR]; @@ -426,11 +426,11 @@ static bool get_proxy_filename(struct anim *anim, return false; } - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); return true; } -static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) +static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *filepath) { char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); @@ -457,7 +457,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname get_index_dir(anim, index_dir, sizeof(index_dir)); - BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); + BLI_join_dirfile(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name); } /* ---------------------------------------------------------------------- @@ -492,18 +492,18 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( { struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output"); - char fname[FILE_MAX]; + char filepath[FILE_MAX]; rv->proxy_size = proxy_size; rv->anim = anim; - get_proxy_filename(rv->anim, rv->proxy_size, fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(rv->anim, rv->proxy_size, filepath, true); + BLI_make_existing_file(filepath); rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); - rv->of->url = av_strdup(fname); + rv->of->url = av_strdup(filepath); fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); @@ -577,7 +577,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( avcodec_parameters_from_context(rv->st->codecpar, rv->c); - int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); + int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE); if (ret < 0) { fprintf(stderr, @@ -723,8 +723,8 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) { - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; if (!ctx) { return; @@ -755,15 +755,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) av_free(ctx->frame); } - get_proxy_filename(ctx->anim, ctx->proxy_size, fname_tmp, true); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath_tmp, true); if (rollback) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - get_proxy_filename(ctx->anim, ctx->proxy_size, fname, false); - unlink(fname); - BLI_rename(fname_tmp, fname); + get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath, false); + unlink(filepath); + BLI_rename(filepath_tmp, filepath); } MEM_freeN(ctx); @@ -907,11 +907,11 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_tc_filename(anim, tc_types[i], fname); + get_tc_filename(anim, tc_types[i], filepath); - context->indexer[i] = IMB_index_builder_create(fname); + context->indexer[i] = IMB_index_builder_create(filepath); if (!context->indexer[i]) { tcs_in_use &= ~tc_types[i]; } @@ -1212,7 +1212,7 @@ typedef struct FallbackIndexBuilderContext { } FallbackIndexBuilderContext; static AviMovie *alloc_proxy_output_avi( - struct anim *anim, char *filename, int width, int height, int quality) + struct anim *anim, char *filepath, int width, int height, int quality) { int x, y; AviFormat format; @@ -1233,7 +1233,7 @@ static AviMovie *alloc_proxy_output_avi( format = AVI_FORMAT_MJPEG; - if (AVI_open_compress(filename, avi, 1, format) != AVI_ERROR_NONE) { + if (AVI_open_compress(filepath, avi, 1, format) != AVI_ERROR_NONE) { MEM_freeN(avi); return NULL; } @@ -1275,13 +1275,13 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; - get_proxy_filename(anim, proxy_sizes[i], fname, true); - BLI_make_existing_file(fname); + get_proxy_filepath(anim, proxy_sizes[i], filepath, true); + BLI_make_existing_file(filepath); context->proxy_ctx[i] = alloc_proxy_output_avi( - anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); + anim, filepath, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality); } } @@ -1291,8 +1291,8 @@ static IndexBuildContext *index_fallback_create_context(struct anim *anim, static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop) { struct anim *anim = context->anim; - char fname[FILE_MAX]; - char fname_tmp[FILE_MAX]; + char filepath[FILE_MAX]; + char filepath_tmp[FILE_MAX]; int i; for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) { @@ -1300,15 +1300,15 @@ static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, AVI_close_compress(context->proxy_ctx[i]); MEM_freeN(context->proxy_ctx[i]); - get_proxy_filename(anim, proxy_sizes[i], fname_tmp, true); - get_proxy_filename(anim, proxy_sizes[i], fname, false); + get_proxy_filepath(anim, proxy_sizes[i], filepath_tmp, true); + get_proxy_filepath(anim, proxy_sizes[i], filepath, false); if (stop) { - unlink(fname_tmp); + unlink(filepath_tmp); } else { - unlink(fname); - rename(fname_tmp, fname); + unlink(filepath); + rename(filepath_tmp, filepath); } } } @@ -1388,7 +1388,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & proxy_sizes_to_build) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } void **filename_key_p; @@ -1411,7 +1411,7 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & built_proxies) { char filename[FILE_MAX]; - if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + if (get_proxy_filepath(anim, proxy_size, filename, false) == false) { return NULL; } printf("Skipping proxy: %s\n", filename); @@ -1532,7 +1532,7 @@ void IMB_anim_set_index_dir(struct anim *anim, const char *dir) struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_proxy_size_to_array_index(preview_size); if (i < 0) { @@ -1547,10 +1547,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) return NULL; } - get_proxy_filename(anim, preview_size, fname, false); + get_proxy_filepath(anim, preview_size, filepath, false); /* proxies are generated in the same color space as animation itself */ - anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace); + anim->proxy_anim[i] = IMB_open_anim(filepath, 0, 0, anim->colorspace); anim->proxies_tried |= preview_size; @@ -1559,7 +1559,7 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size) struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) { - char fname[FILE_MAX]; + char filepath[FILE_MAX]; int i = IMB_timecode_to_array_index(tc); if (i < 0) { @@ -1574,9 +1574,9 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc) return NULL; } - get_tc_filename(anim, tc, fname); + get_tc_filename(anim, tc, filepath); - anim->curr_idx[i] = IMB_indexer_open(fname); + anim->curr_idx[i] = IMB_indexer_open(filepath); anim->indices_tried |= tc; @@ -1602,7 +1602,7 @@ IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim) for (i = 0; i < num_proxy_sizes; i++) { IMB_Proxy_Size proxy_size = proxy_sizes[i]; char filename[FILE_MAX]; - get_proxy_filename(anim, proxy_size, filename, false); + get_proxy_filepath(anim, proxy_size, filename, false); if (BLI_exists(filename)) { existing |= proxy_size; } diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index eb0a2c4a47f..a8150fd1648 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -105,7 +105,7 @@ static int expandrow2( float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z); static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n); static void interleaverow2(float *lptr, const uchar *cptr, int z, int n); -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len); +static int compressrow(const uchar *lbuf, uchar *rlebuf, int z, int row_len); static void lumrow(const uchar *rgbptr, uchar *lumptr, int n); /* @@ -779,18 +779,24 @@ fail: } /** - * Copy an array of ints to an iris image file. - * Each int represents one pixel. xsize and ysize specify the dimensions of - * the pixel array. zsize specifies what kind of image file to - * write out. if zsize is 1, the luminance of the pixels are - * calculated, and a single channel black and white image is saved. - * If zsize is 3, an RGB image file is saved. If zsize is 4, an - * RGBA image file is saved. - * - * Added: zbuf write + * \param filepath: The file path to write to. + * \param lptr: an array of integers to an iris image file (each int represents one pixel). + * \param zptr: depth-buffer (optional, may be NULL). + * \param xsize: with width of the pixel-array. + * \param ysize: height of the pixel-array. + * \param zsize: specifies what kind of image file to write out. + * - 1: the luminance of the pixels are calculated, + * and a single channel black and white image is saved. + * - 3: an RGB image file is saved. + * - 4: an RGBA image file is saved. + * - 8: an RGBA image and a Z-buffer (non-null `zptr`). */ - -static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char *name, int *zptr) +static bool output_iris(const char *filepath, + const uint *lptr, + const int *zptr, + const int xsize, + const int ysize, + const int zsize) { FILE *outf; IMAGE *image; @@ -801,7 +807,7 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char int rlebuflen, goodwrite; goodwrite = 1; - outf = BLI_fopen(name, "wb"); + outf = BLI_fopen(filepath, "wb"); if (!outf) { return 0; } @@ -837,21 +843,20 @@ static bool output_iris(uint *lptr, int xsize, int ysize, int zsize, const char for (z = 0; z < zsize; z++) { if (zsize == 1) { - lumrow((uchar *)lptr, (uchar *)lumbuf, xsize); - len = compressrow((uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); + lumrow((const uchar *)lptr, (uchar *)lumbuf, xsize); + len = compressrow((const uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize); } else { if (z < 4) { - len = compressrow((uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); + len = compressrow((const uchar *)lptr, rlebuf, CHANOFFSET(z), xsize); } else if (z < 8 && zptr) { - len = compressrow((uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); + len = compressrow((const uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize); } } - if (len > rlebuflen) { - fprintf(stderr, "output_iris: rlebuf is too small - bad poop\n"); - exit(1); - } + + BLI_assert_msg(len <= rlebuflen, "The length calculated for 'rlebuflen' was too small!"); + goodwrite *= fwrite(rlebuf, len, 1, outf); starttab[y + z * ysize] = pos; lengthtab[y + z * ysize] = len; @@ -892,9 +897,10 @@ static void lumrow(const uchar *rgbptr, uchar *lumptr, int n) } } -static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int row_len) +static int compressrow(const uchar *lbuf, uchar *rlebuf, const int z, const int row_len) { - uchar *iptr, *ibufend, *sptr, *optr; + const uchar *iptr, *ibufend, *sptr; + uchar *optr; short todo, cc; int count; @@ -964,7 +970,7 @@ bool imb_saveiris(struct ImBuf *ibuf, const char *filepath, int flags) IMB_convert_rgba_to_abgr(ibuf); test_endian_zbuf(ibuf); - const bool ok = output_iris(ibuf->rect, ibuf->x, ibuf->y, zsize, filepath, ibuf->zbuf); + const bool ok = output_iris(filepath, ibuf->rect, ibuf->zbuf, ibuf->x, ibuf->y, zsize); /* restore! Quite clumsy, 2 times a switch... maybe better a malloc ? */ IMB_convert_rgba_to_abgr(ibuf); diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 6fb1fb52153..cffa61977f7 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -39,7 +39,11 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes); static void term_source(j_decompress_ptr cinfo); static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size); static boolean handle_app1(j_decompress_ptr cinfo); -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags); +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height); static const uchar jpeg_default_quality = 75; static uchar ibuf_quality; @@ -246,7 +250,11 @@ static boolean handle_app1(j_decompress_ptr cinfo) return true; } -static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int flags) +static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, + int flags, + int max_size, + size_t *r_width, + size_t *r_height) { JSAMPARRAY row_pointer; JSAMPLE *buffer = NULL; @@ -264,16 +272,34 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo, int fla jpeg_save_markers(cinfo, JPEG_COM, 0xffff); if (jpeg_read_header(cinfo, false) == JPEG_HEADER_OK) { - x = cinfo->image_width; - y = cinfo->image_height; depth = cinfo->num_components; if (cinfo->jpeg_color_space == JCS_YCCK) { cinfo->out_color_space = JCS_CMYK; } + if (r_width) { + *r_width = cinfo->image_width; + } + if (r_height) { + *r_height = cinfo->image_height; + } + + if (max_size > 0) { + /* `libjpeg` can more quickly decompress while scaling down to 1/2, 1/4, 1/8, + * while `libjpeg-turbo` can also do 3/8, 5/8, etc. But max is 1/8. */ + float scale = (float)max_size / MAX2(cinfo->image_width, cinfo->image_height); + cinfo->scale_denom = 8; + cinfo->scale_num = max_uu(1, min_uu(8, ceill(scale * (float)cinfo->scale_denom))); + cinfo->dct_method = JDCT_FASTEST; + cinfo->dither_mode = JDITHER_ORDERED; + } + jpeg_start_decompress(cinfo); + x = cinfo->output_width; + y = cinfo->output_height; + if (flags & IB_test) { jpeg_abort_decompress(cinfo); ibuf = IMB_allocImBuf(x, y, 8 * depth, 0); @@ -449,11 +475,92 @@ ImBuf *imb_load_jpeg(const unsigned char *buffer, jpeg_create_decompress(cinfo); memory_source(cinfo, buffer, size); - ibuf = ibJpegImageFromCinfo(cinfo, flags); + ibuf = ibJpegImageFromCinfo(cinfo, flags, -1, NULL, NULL); + + return ibuf; +} + +/* Defines for JPEG Header markers and segment size. */ +#define JPEG_MARKER_MSB (0xFF) +#define JPEG_MARKER_SOI (0xD8) +#define JPEG_MARKER_APP1 (0xE1) +#define JPEG_APP1_MAX (1 << 16) + +struct ImBuf *imb_thumbnail_jpeg(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[IM_MAX_SPACE], + size_t *r_width, + size_t *r_height) +{ + struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo; + struct my_error_mgr jerr; + FILE *infile = NULL; + + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); + + cinfo->err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = jpeg_error; + + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(cinfo); + return NULL; + } + + if ((infile = BLI_fopen(filepath, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", filepath); + return NULL; + } + + /* If file contains an embedded thumbnail, let's return that instead. */ + + if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) && + (fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) { + /* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */ + unsigned int i = JPEG_APP1_MAX; + /* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */ + while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) && + !feof(infile) && i--) + ; + if (i > 0 && !feof(infile)) { + /* We found a JPEG thumbnail inside this image. */ + ImBuf *ibuf = NULL; + uchar *buffer = MEM_callocN(JPEG_APP1_MAX, "thumbbuffer"); + /* Just put SOI directly in buffer rather than seeking back 2 bytes. */ + buffer[0] = JPEG_MARKER_MSB; + buffer[1] = JPEG_MARKER_SOI; + if (fread(buffer + 2, JPEG_APP1_MAX - 2, 1, infile) == 1) { + ibuf = imb_load_jpeg(buffer, JPEG_APP1_MAX, flags, colorspace); + } + MEM_SAFE_FREE(buffer); + if (ibuf) { + fclose(infile); + return ibuf; + } + } + } + + /* No embedded thumbnail found, so let's create a new one. */ + + fseek(infile, 0, SEEK_SET); + jpeg_create_decompress(cinfo); + + jpeg_stdio_src(cinfo, infile); + ImBuf *ibuf = ibJpegImageFromCinfo(cinfo, flags, max_thumb_size, r_width, r_height); + fclose(infile); return ibuf; } +#undef JPEG_MARKER_MSB +#undef JPEG_MARKER_SOI +#undef JPEG_MARKER_APP1 +#undef JPEG_APP1_MAX + static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf) { JSAMPLE *buffer = NULL; diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 66ee3cf2c26..54ef5438c23 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -10,6 +10,7 @@ #include <cstddef> #include <cstdio> #include <cstdlib> +#include <fcntl.h> #include <fstream> #include <iostream> #include <set> @@ -43,6 +44,8 @@ #include <OpenEXR/ImfInputFile.h> #include <OpenEXR/ImfOutputFile.h> #include <OpenEXR/ImfPixelType.h> +#include <OpenEXR/ImfPreviewImage.h> +#include <OpenEXR/ImfRgbaFile.h> #include <OpenEXR/ImfStandardAttributes.h> #include <OpenEXR/ImfStringAttribute.h> #include <OpenEXR/ImfVersion.h> @@ -63,6 +66,9 @@ #if defined(WIN32) # include "utfconv.h" +# include <io.h> +#else +# include <unistd.h> #endif #include "MEM_guardedalloc.h" @@ -77,7 +83,9 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #endif } #include "BLI_blenlib.h" +#include "BLI_fileops.h" #include "BLI_math_color.h" +#include "BLI_mmap.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -151,6 +159,66 @@ class IMemStream : public Imf::IStream { unsigned char *_exrbuf; }; +/* Memory-Mapped Input Stream */ + +class IMMapStream : public Imf::IStream { + public: + IMMapStream(const char *filepath) : IStream(filepath) + { + int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + if (file < 0) { + throw IEX_NAMESPACE::InputExc("file not found"); + } + _exrpos = 0; + _exrsize = BLI_file_descriptor_size(file); + imb_mmap_lock(); + _mmap_file = BLI_mmap_open(file); + imb_mmap_unlock(); + if (_mmap_file == NULL) { + throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed"); + } + close(file); + _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file); + } + + ~IMMapStream() + { + imb_mmap_lock(); + BLI_mmap_free(_mmap_file); + imb_mmap_unlock(); + } + + /* This is implementing regular `read`, not `readMemoryMapped`, because DWAA and DWAB + * decompressors load on unaligned offsets. Therefore we can't avoid the memory copy. */ + + bool read(char c[], int n) override + { + if (_exrpos + n > _exrsize) { + throw Iex::InputExc("Unexpected end of file."); + } + memcpy(c, _exrbuf + _exrpos, n); + _exrpos += n; + + return _exrpos < _exrsize; + } + + exr_file_offset_t tellg() override + { + return _exrpos; + } + + void seekg(exr_file_offset_t pos) override + { + _exrpos = pos; + } + + private: + BLI_mmap_file *_mmap_file; + exr_file_offset_t _exrpos; + exr_file_offset_t _exrsize; + unsigned char *_exrbuf; +}; + /* File Input Stream */ class IFileStream : public Imf::IStream { @@ -2099,19 +2167,122 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, } } -void imb_initopenexr(void) +struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, + const int UNUSED(flags), + const size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height) { - int num_threads = BLI_system_thread_count(); + IStream *stream = nullptr; + Imf::RgbaInputFile *file = nullptr; + + /* OpenExr uses exceptions for error-handling. */ + try { + + /* The memory-mapped stream is faster, but don't use for huge files as it requires contiguous + * address space and we are processing multiple files at once (typically one per processor + * core). The 100 MB limit here is arbitrary, but seems reasonable and conservative. */ + if (BLI_file_size(filepath) < 100 * 1024 * 1024) { + stream = new IMMapStream(filepath); + } + else { + stream = new IFileStream(filepath); + } + + /* imb_initopenexr() creates a global pool of worker threads. But we thumbnail multiple images + * at once, and by default each file will attempt to use the entire pool for itself, stalling + * the others. So each thumbnail should use a single thread of the pool. */ + file = new RgbaInputFile(*stream, 1); + + if (!file->isComplete()) { + return nullptr; + } + + Imath::Box2i dw = file->dataWindow(); + int source_w = dw.max.x - dw.min.x + 1; + int source_h = dw.max.y - dw.min.y + 1; + *r_width = source_w; + *r_height = source_h; + + /* If there is an embedded thumbnail, return that instead of making a new one. */ + if (file->header().hasPreviewImage()) { + const Imf::PreviewImage &preview = file->header().previewImage(); + ImBuf *ibuf = IMB_allocFromBuffer( + (unsigned int *)preview.pixels(), NULL, preview.width(), preview.height(), 4); + delete file; + delete stream; + IMB_flipy(ibuf); + return ibuf; + } + + /* Create a new thumbnail. */ + + if (colorspace && colorspace[0]) { + colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT); + } + + float scale_factor = MIN2((float)max_thumb_size / (float)source_w, + (float)max_thumb_size / (float)source_h); + int dest_w = (int)(source_w * scale_factor); + int dest_h = (int)(source_h * scale_factor); + + struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rectfloat); + + /* A single row of source pixels. */ + Imf::Array<Imf::Rgba> pixels(source_w); + + /* Loop through destination thumbnail rows. */ + for (int h = 0; h < dest_h; h++) { + + /* Load the single source row that corresponds with destination row. */ + int source_y = (int)((float)h / scale_factor) + dw.min.y; + file->setFrameBuffer(&pixels[0] - dw.min.x - source_y * source_w, 1, source_w); + file->readPixels(source_y); + + for (int w = 0; w < dest_w; w++) { + /* For each destination pixel find single corresponding source pixel. */ + int source_x = (int)(MIN2((w / scale_factor), dw.max.x - 1)); + float *dest_px = &ibuf->rect_float[(h * dest_w + w) * 4]; + dest_px[0] = pixels[source_x].r; + dest_px[1] = pixels[source_x].g; + dest_px[2] = pixels[source_x].b; + dest_px[3] = pixels[source_x].a; + } + } - setGlobalThreadCount(num_threads); + if (file->lineOrder() == INCREASING_Y) { + IMB_flipy(ibuf); + } + + delete file; + delete stream; + + return ibuf; + } + + catch (const std::exception &exc) { + std::cerr << exc.what() << std::endl; + delete file; + delete stream; + return nullptr; + } + + return nullptr; +} + +void imb_initopenexr(void) +{ + /* In a multithreaded program, staticInitialize() must be called once during startup, before the + * program accesses any other functions or classes in the IlmImf library. */ + Imf::staticInitialize(); + Imf::setGlobalThreadCount(BLI_system_thread_count()); } void imb_exitopenexr(void) { - /* Tells OpenEXR to free thread pool, also ensures there is no running - * tasks. - */ - setGlobalThreadCount(0); + /* Tells OpenEXR to free thread pool, also ensures there is no running tasks. */ + Imf::setGlobalThreadCount(0); } } /* export "C" */ diff --git a/source/blender/imbuf/intern/openexr/openexr_api.h b/source/blender/imbuf/intern/openexr/openexr_api.h index c02b03dbe6c..a62c87428b6 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.h +++ b/source/blender/imbuf/intern/openexr/openexr_api.h @@ -26,6 +26,13 @@ bool imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags); struct ImBuf *imb_load_openexr(const unsigned char *mem, size_t size, int flags, char *colorspace); +struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath, + const int flags, + const size_t max_thumb_size, + char colorspace[], + size_t *r_width, + size_t *r_height); + #ifdef __cplusplus } #endif diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index df41c0ca757..4b433836767 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -22,6 +22,8 @@ #include "IMB_filetype.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_metadata.h" +#include "IMB_thumbs.h" #include "imbuf.h" #include "IMB_colormanagement.h" @@ -187,21 +189,21 @@ ImBuf *IMB_loadifffile( return ibuf; } -static void imb_cache_filename(char *filename, const char *name, int flags) +static void imb_cache_filename(char *filepath, const char *name, int flags) { /* read .tx instead if it exists and is not older */ if (flags & IB_tilecache) { - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); - if (!BLI_path_extension_replace(filename, IMB_FILENAME_SIZE, ".tx")) { + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); + if (!BLI_path_extension_replace(filepath, IMB_FILENAME_SIZE, ".tx")) { return; } - if (BLI_file_older(name, filename)) { + if (BLI_file_older(name, filepath)) { return; } } - BLI_strncpy(filename, name, IMB_FILENAME_SIZE); + BLI_strncpy(filepath, name, IMB_FILENAME_SIZE); } ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]) @@ -234,6 +236,61 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S return ibuf; } +struct ImBuf *IMB_thumb_load_image(const char *filepath, + size_t max_thumb_size, + char colorspace[IM_MAX_SPACE]) +{ + const ImFileType *type = IMB_file_type_from_ftype(IMB_ispic_type(filepath)); + if (type == NULL) { + return NULL; + } + + ImBuf *ibuf = NULL; + int flags = IB_rect | IB_metadata; + /* Size of the original image. */ + size_t width = 0; + size_t height = 0; + + char effective_colorspace[IM_MAX_SPACE] = ""; + if (colorspace) { + BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace)); + } + + if (type->load_filepath_thumbnail) { + ibuf = type->load_filepath_thumbnail( + filepath, flags, max_thumb_size, colorspace, &width, &height); + } + else { + /* Skip images of other types if over 100MB. */ + const size_t file_size = BLI_file_size(filepath); + if (file_size != -1 && file_size > THUMB_SIZE_MAX) { + return NULL; + } + ibuf = IMB_loadiffname(filepath, flags, colorspace); + if (ibuf) { + width = ibuf->x; + height = ibuf->y; + } + } + + if (ibuf) { + imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace); + + if (width > 0 && height > 0) { + /* Save dimensions of original image into the thumbnail metadata. */ + char cwidth[40]; + char cheight[40]; + SNPRINTF(cwidth, "%zu", width); + SNPRINTF(cheight, "%zu", height); + IMB_metadata_ensure(&ibuf->metadata); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Width", cwidth); + IMB_metadata_set_field(ibuf->metadata, "Thumb::Image::Height", cheight); + } + } + + return ibuf; +} + ImBuf *IMB_testiffname(const char *filepath, int flags) { ImBuf *ibuf; diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 37734ebacb2..f2c9c82fa66 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -318,12 +318,8 @@ static ImBuf *thumb_create_ex(const char *file_path, char tpath[FILE_MAX]; char tdir[FILE_MAX]; char temp[FILE_MAX]; - char mtime[40] = "0"; /* in case we can't stat the file */ - char cwidth[40] = "0"; /* in case images have no data */ - char cheight[40] = "0"; + char mtime[40] = "0"; /* in case we can't stat the file */ short tsize = 128; - short ex, ey; - float scaledx, scaledy; BLI_stat_t info; switch (size) { @@ -340,15 +336,6 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; /* unknown size */ } - /* exception, skip images over 100mb */ - if (source == THB_SOURCE_IMAGE) { - const size_t file_size = BLI_file_size(file_path); - if (file_size != -1 && file_size > THUMB_SIZE_MAX) { - // printf("file too big: %d, skipping %s\n", (int)size, file_path); - return NULL; - } - } - if (get_thumb_dir(tdir, size)) { BLI_snprintf(tpath, FILE_MAX, "%s%s", tdir, thumb); // thumb[8] = '\0'; /* shorten for tempname, not needed anymore */ @@ -368,7 +355,7 @@ static ImBuf *thumb_create_ex(const char *file_path, if (img == NULL) { switch (source) { case THB_SOURCE_IMAGE: - img = IMB_loadiffname(file_path, IB_rect | IB_metadata, NULL); + img = IMB_thumb_load_image(file_path, tsize, NULL); break; case THB_SOURCE_BLEND: img = IMB_thumb_load_blend(file_path, blen_group, blen_id); @@ -385,8 +372,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (BLI_stat(file_path, &info) != -1) { BLI_snprintf(mtime, sizeof(mtime), "%ld", (long int)info.st_mtime); } - BLI_snprintf(cwidth, sizeof(cwidth), "%d", img->x); - BLI_snprintf(cheight, sizeof(cheight), "%d", img->y); } } else if (THB_SOURCE_MOVIE == source) { @@ -411,28 +396,20 @@ static ImBuf *thumb_create_ex(const char *file_path, return NULL; } - if (img->x > img->y) { - scaledx = (float)tsize; - scaledy = ((float)img->y / (float)img->x) * tsize; - } - else { - scaledy = (float)tsize; - scaledx = ((float)img->x / (float)img->y) * tsize; - } - /* Scaling down must never assign zero width/height, see: T89868. */ - ex = MAX2(1, (short)scaledx); - ey = MAX2(1, (short)scaledy); - - /* save some time by only scaling byte buf */ - if (img->rect_float) { - if (img->rect == NULL) { - IMB_rect_from_float(img); + if (img->x > tsize || img->y > tsize) { + float scale = MIN2((float)tsize / (float)img->x, (float)tsize / (float)img->y); + /* Scaling down must never assign zero width/height, see: T89868. */ + short ex = MAX2(1, (short)(img->x * scale)); + short ey = MAX2(1, (short)(img->y * scale)); + /* Save some time by only scaling byte buf */ + if (img->rect_float) { + if (img->rect == NULL) { + IMB_rect_from_float(img); + } + imb_freerectfloatImBuf(img); } - - imb_freerectfloatImBuf(img); + IMB_scaleImBuf(img, ex, ey); } - - IMB_scaleImBuf(img, ex, ey); } BLI_snprintf(desc, sizeof(desc), "Thumbnail for %s", uri); IMB_metadata_ensure(&img->metadata); @@ -443,10 +420,6 @@ static ImBuf *thumb_create_ex(const char *file_path, if (use_hash) { IMB_metadata_set_field(img->metadata, "X-Blender::Hash", hash); } - if (ELEM(source, THB_SOURCE_IMAGE, THB_SOURCE_BLEND, THB_SOURCE_FONT)) { - IMB_metadata_set_field(img->metadata, "Thumb::Image::Width", cwidth); - IMB_metadata_set_field(img->metadata, "Thumb::Image::Height", cheight); - } img->ftype = IMB_FTYPE_PNG; img->planes = 32; @@ -493,27 +466,27 @@ static ImBuf *thumb_create_or_fail(const char *file_path, return img; } -ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, ImBuf *img) +ImBuf *IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, ImBuf *img) { char uri[URI_MAX] = ""; char thumb_name[40]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } thumbname_from_uri(uri, thumb_name, sizeof(thumb_name)); return thumb_create_ex( - path, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); + filepath, uri, thumb_name, false, THUMB_DEFAULT_HASH, NULL, NULL, size, source, img); } -ImBuf *IMB_thumb_read(const char *path, ThumbSize size) +ImBuf *IMB_thumb_read(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; ImBuf *img = NULL; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return NULL; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { @@ -523,16 +496,16 @@ ImBuf *IMB_thumb_read(const char *path, ThumbSize size) return img; } -void IMB_thumb_delete(const char *path, ThumbSize size) +void IMB_thumb_delete(const char *filepath, ThumbSize size) { char thumb[FILE_MAX]; char uri[URI_MAX]; - if (!uri_from_filename(path, uri)) { + if (!uri_from_filename(filepath, uri)) { return; } if (thumbpath_from_uri(uri, thumb, sizeof(thumb), size)) { - if (BLI_path_ncmp(path, thumb, sizeof(thumb)) == 0) { + if (BLI_path_ncmp(filepath, thumb, sizeof(thumb)) == 0) { return; } if (BLI_exists(thumb)) { diff --git a/source/blender/imbuf/intern/thumbs_font.c b/source/blender/imbuf/intern/thumbs_font.c index 7a1c4947c99..c0a33f608a5 100644 --- a/source/blender/imbuf/intern/thumbs_font.c +++ b/source/blender/imbuf/intern/thumbs_font.c @@ -41,7 +41,7 @@ void IMB_thumb_ensure_translations(void) } } -struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y) +struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y) { const int font_size = y / 4; @@ -60,7 +60,7 @@ struct ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned /* draw with full alpha */ font_color[3] = 1.0f; - BLF_thumb_preview(filename, + BLF_thumb_preview(filepath, thumb_str, i18n_thumb_str, ARRAY_SIZE(thumb_str), diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 45b50c866fe..ffa989a29b4 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -390,14 +390,3 @@ bool IMB_isanim(const char *filepath) return (type && type != ANIM_SEQUENCE); } - -bool IMB_isfloat(const ImBuf *ibuf) -{ - const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->flag & IM_FTYPE_FLOAT) { - return true; - } - } - return false; -} diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 8da9eb9ccf7..8e004938a89 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -197,12 +197,10 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex, GPUTexture *IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, - bool use_premult, - bool limit_gl_texture_size) + bool use_premult) { GPUTexture *tex = NULL; - int size[2] = {GPU_texture_size_with_limit(ibuf->x, limit_gl_texture_size), - GPU_texture_size_with_limit(ibuf->y, limit_gl_texture_size)}; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); #ifdef WITH_DDS diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 56c9384a330..d2c0b61c1c5 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -19,11 +19,6 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -static bool prepare_write_imbuf(const ImFileType *type, ImBuf *ibuf) -{ - return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); -} - bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) { errno = 0; @@ -36,34 +31,22 @@ bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags) ibuf->flags = flags; const ImFileType *type = IMB_file_type_from_ibuf(ibuf); - if (type != NULL) { - if (type->save != NULL) { - prepare_write_imbuf(type, ibuf); - return type->save(ibuf, filepath, flags); - } + if (type == NULL || type->save == NULL) { + fprintf(stderr, "Couldn't save picture.\n"); + return false; } - fprintf(stderr, "Couldn't save picture.\n"); - - return false; -} - -bool IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) -{ - bool changed = false; - - if (isfloat) { - /* pass */ - } - else { + /* If writing byte image from float buffer, create a byte buffer for writing. + * + * For color managed image writing, IMB_colormanagement_imbuf_for_write should + * have already created this byte buffer. This is a basic fallback for other + * cases where we do not have a specific desired output colorspace. */ + if (!(type->flag & IM_FTYPE_FLOAT)) { if (ibuf->rect == NULL && ibuf->rect_float) { ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); IMB_rect_from_float(ibuf); - if (ibuf->rect != NULL) { - changed = true; - } } } - return changed; + return type->save(ibuf, filepath, flags); } diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index c1ff7df0c37..edaf53b3efa 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -115,7 +115,7 @@ static void export_startjob(void *customdata, return; } - ABCHierarchyIterator iter(data->depsgraph, abc_archive.get(), data->params); + ABCHierarchyIterator iter(data->bmain, data->depsgraph, abc_archive.get(), data->params); if (export_animation) { CLOG_INFO(&LOG, 2, "Exporting animation"); diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index d33adfdb4b9..ab62694b802 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -26,10 +26,11 @@ namespace blender::io::alembic { -ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph, +ABCHierarchyIterator::ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive, const AlembicExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), abc_archive_(abc_archive), params_(params) { } diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index 24d3f319fbd..f7814c28a55 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -13,6 +13,7 @@ #include <Alembic/Abc/OObject.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::alembic { @@ -36,7 +37,8 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator { const AlembicExportParams ¶ms_; public: - ABCHierarchyIterator(Depsgraph *depsgraph, + ABCHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, ABCArchive *abc_archive_, const AlembicExportParams ¶ms); diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index 45bf898f3f5..633611cf1a6 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -48,9 +48,9 @@ static const std::string propNameOriginalCoordinates("Pref"); static void get_uvs(const CDStreamConfig &config, std::vector<Imath::V2f> &uvs, std::vector<uint32_t> &uvidx, - void *cd_data) + const void *cd_data) { - MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data); + const MLoopUV *mloopuv_array = static_cast<const MLoopUV *>(cd_data); if (!mloopuv_array) { return; @@ -68,7 +68,7 @@ static void get_uvs(const CDStreamConfig &config, /* Iterate in reverse order to match exported polygons. */ for (int i = 0; i < num_poly; i++) { MPoly ¤t_poly = polygons[i]; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; for (int j = 0; j < current_poly.totloop; j++, count++) { loopuv--; @@ -87,7 +87,7 @@ static void get_uvs(const CDStreamConfig &config, for (int i = 0; i < num_poly; i++) { MPoly ¤t_poly = polygons[i]; MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; for (int j = 0; j < current_poly.totloop; j++) { looppoly--; @@ -125,7 +125,7 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom return ""; } - void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + const void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); get_uvs(config, sample.uvs, sample.indices, cd_data); @@ -139,7 +139,7 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom */ static void write_uv(const OCompoundProperty &prop, CDStreamConfig &config, - void *data, + const void *data, const char *name) { std::vector<uint32_t> indices; @@ -169,12 +169,12 @@ static void write_uv(const OCompoundProperty &prop, static void get_cols(const CDStreamConfig &config, std::vector<Imath::C4f> &buffer, std::vector<uint32_t> &uvidx, - void *cd_data) + const void *cd_data) { const float cscale = 1.0f / 255.0f; - MPoly *polys = config.mpoly; - MLoop *mloops = config.mloop; - MCol *cfaces = static_cast<MCol *>(cd_data); + const MPoly *polys = config.mpoly; + const MLoop *mloops = config.mloop; + const MCol *cfaces = static_cast<const MCol *>(cd_data); buffer.reserve(config.totvert); uvidx.reserve(config.totvert); @@ -182,9 +182,9 @@ static void get_cols(const CDStreamConfig &config, Imath::C4f col; for (int i = 0; i < config.totpoly; i++) { - MPoly *p = &polys[i]; - MCol *cface = &cfaces[p->loopstart + p->totloop]; - MLoop *mloop = &mloops[p->loopstart + p->totloop]; + const MPoly *p = &polys[i]; + const MCol *cface = &cfaces[p->loopstart + p->totloop]; + const MLoop *mloop = &mloops[p->loopstart + p->totloop]; for (int j = 0; j < p->totloop; j++) { cface--; @@ -207,7 +207,7 @@ static void get_cols(const CDStreamConfig &config, */ static void write_mcol(const OCompoundProperty &prop, CDStreamConfig &config, - void *data, + const void *data, const char *name) { std::vector<uint32_t> indices; @@ -283,7 +283,7 @@ void write_custom_data(const OCompoundProperty &prop, const int tot_layers = CustomData_number_of_layers(data, cd_data_type); for (int i = 0; i < tot_layers; i++) { - void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); const char *name = CustomData_get_layer_name(data, cd_data_type, i); if (cd_data_type == CD_MLOOPUV) { diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 8c62484028d..219bba285a7 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -377,25 +377,22 @@ BLI_INLINE void read_uvs_params(CDStreamConfig &config, static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) { CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); - void *cd_ptr; - CustomData *loopdata; - int numloops; /* unsupported custom data type -- don't do anything. */ if (!ELEM(cd_data_type, CD_MLOOPUV, CD_PROP_BYTE_COLOR)) { return nullptr; } - loopdata = &mesh->ldata; - cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + void *cd_ptr = CustomData_get_layer_named(&mesh->ldata, cd_data_type, name); if (cd_ptr != nullptr) { /* layer already exists, so just return it. */ return cd_ptr; } /* Create a new layer. */ - numloops = mesh->totloop; - cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); + int numloops = mesh->totloop; + cd_ptr = CustomData_add_layer_named( + &mesh->ldata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); return cd_ptr; } diff --git a/source/blender/io/collada/AnimationImporter.h b/source/blender/io/collada/AnimationImporter.h index 7c5b38f5f8d..1c5b7570b9d 100644 --- a/source/blender/io/collada/AnimationImporter.h +++ b/source/blender/io/collada/AnimationImporter.h @@ -228,7 +228,7 @@ class AnimationImporter : private TransformReader, public AnimationImporterBase COLLADAFW::Transformation::TransformationType tm_type); /** * Internal, better make it private - * warning: evaluates only rotation and only assigns matrix transforms now + * WARNING: evaluates only rotation and only assigns matrix transforms now * prerequisites: animlist_map, curve_map. */ void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra); diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 3952a4ae334..7e2a24aeb41 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -477,7 +477,8 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) for (int a = 0; a < totlayer_mcol; a++) { map_index++; - MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, a); + const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer_n( + &me->ldata, CD_PROP_BYTE_COLOR, a); COLLADASW::FloatSourceF source(mSW); @@ -502,7 +503,7 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) MPoly *mpoly; int i; for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) { - MLoopCol *mlc = mloopcol + mpoly->loopstart; + const MLoopCol *mlc = mloopcol + mpoly->loopstart; for (int j = 0; j < mpoly->totloop; j++, mlc++) { source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f); } @@ -619,7 +620,7 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals, MVert *verts = me->mvert; const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me); MLoop *mloops = me->mloop; - float(*lnors)[3] = nullptr; + const float(*lnors)[3] = nullptr; bool use_custom_normals = false; BKE_mesh_calc_normals_split(me); diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h index 79b9778738d..85f960abfe6 100644 --- a/source/blender/io/collada/MeshImporter.h +++ b/source/blender/io/collada/MeshImporter.h @@ -142,7 +142,7 @@ class MeshImporter : public MeshImporterBase { /** * Return the number of faces by summing up * the face-counts of the parts. - * hint: This is done because `mesh->getFacesCount()` does + * HINT: This is done because `mesh->getFacesCount()` does * count loose edges as extra faces, which is not what we want here. */ void allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me); @@ -150,7 +150,7 @@ class MeshImporter : public MeshImporterBase { /* TODO: import uv set names */ /** * Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON - * Important: This function MUST be called before read_lines() + * IMPORTANT: This function MUST be called before read_lines() * Otherwise we will lose all edges from faces (see read_lines() above) * * TODO: import uv set names. @@ -158,7 +158,7 @@ class MeshImporter : public MeshImporterBase { void read_polys(COLLADAFW::Mesh *mesh, Mesh *me); /** * Read all loose edges. - * Important: This function assumes that all edges from existing + * IMPORTANT: This function assumes that all edges from existing * faces have already been generated and added to me->medge * So this function MUST be called after read_faces() (see below) */ diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index b5766b44025..e80bd850825 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -8,7 +8,6 @@ set(INC ../../depsgraph ../../makesdna ../../../../intern/guardedalloc - ../../../../extern/fast_float ) set(INC_SYS @@ -20,13 +19,11 @@ set(SRC intern/dupli_persistent_id.cc intern/object_identifier.cc intern/path_util.cc - intern/string_utils.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh IO_path_util.hh IO_path_util_types.h - IO_string_utils.hh IO_types.h intern/dupli_parent_finder.hh ) @@ -45,7 +42,6 @@ if(WITH_GTESTS) intern/abstract_hierarchy_iterator_test.cc intern/hierarchy_context_order_test.cc intern/object_identifier_test.cc - intern/string_utils_test.cc ) set(TEST_INC ../../blenloader diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 3c6b6cc631e..3371501db95 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -30,6 +30,7 @@ struct Depsgraph; struct DupliObject; struct ID; +struct Main; struct Object; struct ParticleSystem; @@ -204,12 +205,13 @@ class AbstractHierarchyIterator { protected: ExportGraph export_graph_; ExportPathMap duplisource_export_path_; + Main *bmain_; Depsgraph *depsgraph_; WriterMap writers_; ExportSubset export_subset_; public: - explicit AbstractHierarchyIterator(Depsgraph *depsgraph); + explicit AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph); virtual ~AbstractHierarchyIterator(); /* Iterate over the depsgraph, create writers, and tell the writers to write. diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/common/IO_string_utils.hh deleted file mode 100644 index 25f1f01c6ed..00000000000 --- a/source/blender/io/common/IO_string_utils.hh +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -#include "BLI_string_ref.hh" - -/* - * Various text parsing utilities commonly used by text-based input formats. - */ - -namespace blender::io { - -/** - * Fetches next line from an input string buffer. - * - * The returned line will not have '\n' characters at the end; - * the `buffer` is modified to contain remaining text without - * the input line. - * - * Note that backslash (\) character is treated as a line - * continuation, similar to OBJ file format or a C preprocessor. - */ -StringRef read_next_line(StringRef &buffer); - -/** - * Drop leading white-space from a StringRef. - * Note that backslash character is considered white-space. - */ -StringRef drop_whitespace(StringRef str); - -/** - * Drop leading non-white-space from a StringRef. - * Note that backslash character is considered white-space. - */ -StringRef drop_non_whitespace(StringRef str); - -/** - * Parse an integer from an input string. - * The parsed result is stored in `dst`. The function skips - * leading white-space unless `skip_space=false`. If the - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space = true); - -/** - * Parse a float from an input string. - * The parsed result is stored in `dst`. The function skips - * leading white-space unless `skip_space=false`. If the - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space = true); - -/** - * Parse a number of white-space separated floats from an input string. - * The parsed `count` numbers are stored in `dst`. If a - * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. - * - * Returns the remainder of the input string after parsing. - */ -StringRef parse_floats(StringRef str, float fallback, float *dst, int count); - -} // namespace blender::io diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index f0501d4cf62..82bb1c57833 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -161,8 +161,8 @@ bool AbstractHierarchyWriter::check_has_deforming_physics(const HierarchyContext return rbo != nullptr && rbo->type == RBO_TYPE_ACTIVE && (rbo->flag & RBO_FLAG_USE_DEFORM) != 0; } -AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), export_subset_({true, true}) +AbstractHierarchyIterator::AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : bmain_(bmain), depsgraph_(depsgraph), export_subset_({true, true}) { } diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc index 52cae89c2e8..81a3546259e 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator_test.cc @@ -54,7 +54,8 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator { used_writers hair_writers; used_writers particle_writers; - explicit TestingHierarchyIterator(Depsgraph *depsgraph) : AbstractHierarchyIterator(depsgraph) + explicit TestingHierarchyIterator(Main *bmain, Depsgraph *depsgraph) + : AbstractHierarchyIterator(bmain, depsgraph) { } ~TestingHierarchyIterator() override @@ -105,7 +106,7 @@ class AbstractHierarchyIteratorTest : public BlendfileLoadingBaseTest { /* Create a test iterator. */ void iterator_create() { - iterator = new TestingHierarchyIterator(depsgraph); + iterator = new TestingHierarchyIterator(bfile->main, depsgraph); } /* Free the test iterator if it is not nullptr. */ void iterator_free() diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/common/intern/string_utils.cc deleted file mode 100644 index 3a12250e14b..00000000000 --- a/source/blender/io/common/intern/string_utils.cc +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "IO_string_utils.hh" - -/* Note: we could use C++17 <charconv> from_chars to parse - * floats, but even if some compilers claim full support, - * their standard libraries are not quite there yet. - * LLVM/libc++ only has a float parser since LLVM 14, - * and gcc/libstdc++ since 11.1. So until at least these are - * the minimum spec, use an external library. */ -#include "fast_float.h" -#include <charconv> - -namespace blender::io { - -StringRef read_next_line(StringRef &buffer) -{ - const char *start = buffer.begin(); - const char *end = buffer.end(); - size_t len = 0; - char prev = 0; - const char *ptr = start; - while (ptr < end) { - char c = *ptr++; - if (c == '\n' && prev != '\\') { - break; - } - prev = c; - ++len; - } - - buffer = StringRef(ptr, end); - return StringRef(start, len); -} - -static bool is_whitespace(char c) -{ - return c <= ' ' || c == '\\'; -} - -StringRef drop_whitespace(StringRef str) -{ - while (!str.is_empty() && is_whitespace(str[0])) { - str = str.drop_prefix(1); - } - return str; -} - -StringRef drop_non_whitespace(StringRef str) -{ - while (!str.is_empty() && !is_whitespace(str[0])) { - str = str.drop_prefix(1); - } - return str; -} - -static StringRef drop_plus(StringRef str) -{ - if (!str.is_empty() && str[0] == '+') { - str = str.drop_prefix(1); - } - return str; -} - -StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space) -{ - if (skip_space) { - str = drop_whitespace(str); - } - str = drop_plus(str); - fast_float::from_chars_result res = fast_float::from_chars(str.begin(), str.end(), dst); - if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { - dst = fallback; - } - return StringRef(res.ptr, str.end()); -} - -StringRef parse_floats(StringRef str, float fallback, float *dst, int count) -{ - for (int i = 0; i < count; ++i) { - str = parse_float(str, fallback, dst[i]); - } - return str; -} - -StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space) -{ - if (skip_space) { - str = drop_whitespace(str); - } - str = drop_plus(str); - std::from_chars_result res = std::from_chars(str.begin(), str.end(), dst); - if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { - dst = fallback; - } - return StringRef(res.ptr, str.end()); -} - -} // namespace blender::io diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 594dbe5ddd1..1205ae74e6f 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -70,6 +70,7 @@ set(SRC intern/usd_writer_mesh.cc intern/usd_writer_metaball.cc intern/usd_writer_transform.cc + intern/usd_writer_volume.cc intern/usd_reader_camera.cc intern/usd_reader_curve.cc @@ -96,6 +97,7 @@ set(SRC intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h + intern/usd_writer_volume.h intern/usd_reader_camera.h intern/usd_reader_curve.h @@ -121,8 +123,15 @@ list(APPEND LIB ${BOOST_LIBRARIES} ) -list(APPEND LIB -) +if(WITH_OPENVDB) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) +endif() blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc index d7c1a5adc7c..2049c631671 100644 --- a/source/blender/io/usd/intern/usd_capi_export.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -38,7 +38,7 @@ struct ExportJobData { Depsgraph *depsgraph; wmWindowManager *wm; - char filename[FILE_MAX]; + char filepath[FILE_MAX]; USDExportParams params; bool export_ok; @@ -74,13 +74,13 @@ static void export_startjob(void *customdata, /* For restoring the current frame after exporting animation is done. */ const int orig_frame = CFRA; - pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); + pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filepath); if (!usd_stage) { /* This happens when the USD JSON files cannot be found. When that happens, * the USD library doesn't know it has the functionality to write USDA and * USDC files, and creating a new UsdStage fails. */ WM_reportf( - RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); + RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filepath); return; } @@ -97,7 +97,7 @@ static void export_startjob(void *customdata, usd_stage->SetEndTimeCode(scene->r.efra); } - USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params); if (data->params.export_animation) { /* Writing the animated frames is not 100% of the work, but it's our best guess. */ @@ -145,8 +145,8 @@ static void export_endjob(void *customdata) DEG_graph_free(data->depsgraph); - if (!data->export_ok && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); + if (!data->export_ok && BLI_exists(data->filepath)) { + BLI_delete(data->filepath, false, false); } G.is_rendering = false; @@ -171,7 +171,7 @@ bool USD_export(bContext *C, job->bmain = CTX_data_main(C); job->wm = CTX_wm_manager(C); job->export_ok = false; - BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + BLI_strncpy(job->filepath, filepath, sizeof(job->filepath)); job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); job->params = *params; diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 58cd7d5014e..29b256125f0 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -119,7 +119,7 @@ struct ImportJobData { ViewLayer *view_layer; wmWindowManager *wm; - char filename[1024]; + char filepath[1024]; USDImportParams params; ImportSettings settings; @@ -150,7 +150,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo if (data->params.create_collection) { char display_name[1024]; BLI_path_to_display_name( - display_name, strlen(data->filename), BLI_path_basename(data->filename)); + display_name, strlen(data->filepath), BLI_path_basename(data->filepath)); Collection *import_collection = BKE_collection_add( data->bmain, data->scene->master_collection, display_name); id_fake_user_set(&import_collection->id); @@ -164,10 +164,10 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo data->view_layer, import_collection); } - BLI_path_abs(data->filename, BKE_main_blendfile_path_from_global()); + BLI_path_abs(data->filepath, BKE_main_blendfile_path_from_global()); CacheFile *cache_file = static_cast<CacheFile *>( - BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + BKE_cachefile_add(data->bmain, BLI_path_basename(data->filepath))); /* Decrement the ID ref-count because it is going to be incremented for each * modifier and constraint that it will be attached to, so since currently @@ -176,7 +176,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo cache_file->is_sequence = data->params.is_sequence; cache_file->scale = data->params.scale; - STRNCPY(cache_file->filepath, data->filename); + STRNCPY(cache_file->filepath, data->filepath); data->settings.cache_file = cache_file; @@ -191,10 +191,10 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo *data->do_update = true; *data->progress = 0.1f; - pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filename); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filepath); if (!stage) { - WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filename); + WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filepath); data->import_ok = false; return; } @@ -356,7 +356,7 @@ bool USD_import(struct bContext *C, job->view_layer = CTX_data_view_layer(C); job->wm = CTX_wm_manager(C); job->import_ok = false; - BLI_strncpy(job->filename, filepath, 1024); + BLI_strncpy(job->filepath, filepath, 1024); job->settings.scale = params->scale; job->settings.sequence_offset = params->offset; @@ -499,13 +499,13 @@ void USD_CacheReader_free(CacheReader *reader) } CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, - const char *filename, + const char *filepath, 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); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filepath); if (!stage) { return nullptr; diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h index 5a3f94d01f8..a636d849296 100644 --- a/source/blender/io/usd/intern/usd_exporter_context.h +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -8,12 +8,14 @@ #include <pxr/usd/usd/common.h> struct Depsgraph; +struct Main; namespace blender::io::usd { class USDHierarchyIterator; struct USDExporterContext { + Main *bmain; Depsgraph *depsgraph; const pxr::UsdStageRefPtr stage; const pxr::SdfPath usd_path; diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index 7a0d896fb3e..51261c4d91e 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -10,6 +10,7 @@ #include "usd_writer_mesh.h" #include "usd_writer_metaball.h" #include "usd_writer_transform.h" +#include "usd_writer_volume.h" #include <string> @@ -28,10 +29,11 @@ namespace blender::io::usd { -USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, +USDHierarchyIterator::USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params) + : AbstractHierarchyIterator(bmain, depsgraph), stage_(stage), params_(params) { } @@ -59,6 +61,15 @@ void USDHierarchyIterator::set_export_frame(float frame_nr) export_time_ = pxr::UsdTimeCode(frame_nr); } +std::string USDHierarchyIterator::get_export_file_path() const +{ + /* Returns the same path that was passed to `stage_` object during it's creation (via + * `pxr::UsdStage::CreateNew` function). */ + const pxr::SdfLayerHandle root_layer = stage_->GetRootLayer(); + const std::string usd_export_file_path = root_layer->GetRealPath(); + return usd_export_file_path; +} + const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const { return export_time_; @@ -66,7 +77,8 @@ const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context) { - return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; + return USDExporterContext{ + bmain_, depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; } AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer( @@ -93,6 +105,9 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch case OB_MBALL: data_writer = new USDMetaballWriter(usd_export_context); break; + case OB_VOLUME: + data_writer = new USDVolumeWriter(usd_export_context); + break; case OB_EMPTY: case OB_CURVES_LEGACY: diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h index d5566103af4..445c37238be 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.h +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -12,6 +12,7 @@ #include <pxr/usd/usd/timeCode.h> struct Depsgraph; +struct Main; struct Object; namespace blender::io::usd { @@ -27,11 +28,13 @@ class USDHierarchyIterator : public AbstractHierarchyIterator { const USDExportParams ¶ms_; public: - USDHierarchyIterator(Depsgraph *depsgraph, + USDHierarchyIterator(Main *bmain, + Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams ¶ms); void set_export_frame(float frame_nr); + std::string get_export_file_path() const; const pxr::UsdTimeCode &get_export_time_code() const; virtual std::string make_valid_name(const std::string &name) const override; diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index dbeb9df1ee8..2be9b1c065a 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -47,6 +47,11 @@ bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const return true; } +std::string USDAbstractWriter::get_export_file_path() const +{ + return usd_export_context_.hierarchy_iterator->get_export_file_path(); +} + pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const { if (is_animated_) { diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h index 66feb6e1070..8adc054c2c2 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.h +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -51,6 +51,7 @@ class USDAbstractWriter : public AbstractHierarchyWriter { protected: virtual void do_write(HierarchyContext &context) = 0; + std::string get_export_file_path() const; pxr::UsdTimeCode get_export_time_code() const; pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material); diff --git a/source/blender/io/usd/intern/usd_writer_material.cc b/source/blender/io/usd/intern/usd_writer_material.cc index dd795e4f922..857896b9330 100644 --- a/source/blender/io/usd/intern/usd_writer_material.cc +++ b/source/blender/io/usd/intern/usd_writer_material.cc @@ -575,7 +575,7 @@ static std::string get_tex_image_asset_path(bNode *node, char file_path[FILE_MAX]; BLI_split_file_part(path.c_str(), file_path, FILE_MAX); - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path, nullptr); } else { @@ -593,7 +593,7 @@ static std::string get_tex_image_asset_path(bNode *node, return exp_path; } - if (export_params.relative_texture_paths) { + if (export_params.relative_paths) { /* Get the path relative to the USD. */ pxr::SdfLayerHandle layer = stage->GetRootLayer(); std::string stage_path = layer->GetRealPath(); @@ -605,11 +605,7 @@ static std::string get_tex_image_asset_path(bNode *node, strcpy(rel_path, path.c_str()); BLI_path_rel(rel_path, stage_path.c_str()); - - /* BLI_path_rel adds '//' as a prefix to the path, if - * generating the relative path was successful. */ - if (rel_path[0] != '/' || rel_path[1] != '/') { - /* No relative path generated. */ + if (!BLI_path_is_rel(rel_path)) { return path; } diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc new file mode 100644 index 00000000000..4126be6966a --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.cc @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "usd_writer_volume.h" + +#include <pxr/base/tf/pathUtils.h> +#include <pxr/usd/usdVol/openVDBAsset.h> +#include <pxr/usd/usdVol/volume.h> + +#include "DNA_volume_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_volume.h" + +#include "BLI_fileops.h" +#include "BLI_index_range.hh" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "usd_hierarchy_iterator.h" + +namespace blender::io::usd { + +USDVolumeWriter::USDVolumeWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDVolumeWriter::check_is_animated(const HierarchyContext &context) const +{ + const Volume *volume = static_cast<Volume *>(context.object->data); + return volume->is_sequence; +} + +void USDVolumeWriter::do_write(HierarchyContext &context) +{ + Volume *volume = static_cast<Volume *>(context.object->data); + if (!BKE_volume_load(volume, usd_export_context_.bmain)) { + return; + } + + const int num_grids = BKE_volume_num_grids(volume); + if (!num_grids) { + return; + } + + auto vdb_file_path = resolve_vdb_file(volume); + if (!vdb_file_path.has_value()) { + WM_reportf(RPT_WARNING, + "USD Export: failed to resolve .vdb file for object: %s", + volume->id.name + 2); + return; + } + + if (usd_export_context_.export_params.relative_paths) { + if (auto relative_vdb_file_path = construct_vdb_relative_file_path(*vdb_file_path)) { + vdb_file_path = relative_vdb_file_path; + } + else { + WM_reportf(RPT_WARNING, + "USD Export: couldn't construct relative file path for .vdb file, absolute path " + "will be used instead"); + } + } + + const pxr::UsdTimeCode timecode = get_export_time_code(); + const pxr::SdfPath &volume_path = usd_export_context_.usd_path; + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + pxr::UsdVolVolume usd_volume = pxr::UsdVolVolume::Define(stage, volume_path); + + for (const int i : IndexRange(num_grids)) { + const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i); + const std::string grid_name = BKE_volume_grid_name(grid); + const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name); + const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id)); + pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path); + usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode); + usd_grid.GetFilePathAttr().Set(pxr::SdfAssetPath(*vdb_file_path), timecode); + usd_volume.CreateFieldRelationship(pxr::TfToken(grid_id), grid_path); + } + + float3 volume_bound_min(std::numeric_limits<float>::max()); + float3 volume_bound_max(std::numeric_limits<float>::min()); + if (BKE_volume_min_max(volume, volume_bound_min, volume_bound_max)) { + const pxr::VtArray<pxr::GfVec3f> volume_extent = {pxr::GfVec3f(&volume_bound_min[0]), + pxr::GfVec3f(&volume_bound_max[0])}; + usd_volume.GetExtentAttr().Set(volume_extent, timecode); + } + + BKE_volume_unload(volume); +} + +std::optional<std::string> USDVolumeWriter::resolve_vdb_file(const Volume *volume) const +{ + std::optional<std::string> vdb_file_path; + if (volume->filepath[0] == '\0') { + /* Entering this section should mean that Volume object contains OpenVDB data that is not + * obtained from external .vdb file but rather generated inside of Blender (i.e. by 'Mesh to + * Volume' modifier). Try to save this data to a .vdb file. */ + + vdb_file_path = construct_vdb_file_path(volume); + if (!BKE_volume_save( + volume, usd_export_context_.bmain, NULL, vdb_file_path.value_or("").c_str())) { + return std::nullopt; + } + } + + if (!vdb_file_path.has_value()) { + vdb_file_path = BKE_volume_grids_frame_filepath(volume); + if (vdb_file_path->empty()) { + return std::nullopt; + } + } + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume *volume) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char usd_directory_path[FILE_MAX]; + char usd_file_name[FILE_MAXFILE]; + BLI_split_dirfile(usd_file_path.c_str(), + usd_directory_path, + usd_file_name, + sizeof(usd_directory_path), + sizeof(usd_file_name)); + + if (usd_directory_path[0] == '\0' || usd_file_name[0] == '\0') { + return std::nullopt; + } + + const char *vdb_directory_name = "volumes"; + + char vdb_directory_path[FILE_MAX]; + BLI_strncpy(vdb_directory_path, usd_directory_path, FILE_MAX); + strcat(vdb_directory_path, vdb_directory_name); + BLI_dir_create_recursive(vdb_directory_path); + + char vdb_file_name[FILE_MAXFILE]; + BLI_strncpy(vdb_file_name, volume->id.name + 2, FILE_MAXFILE); + const pxr::UsdTimeCode timecode = get_export_time_code(); + if (!timecode.IsDefault()) { + const int frame = (int)timecode.GetValue(); + const int num_frame_digits = frame == 0 ? 1 : integer_digits_i(abs(frame)); + BLI_path_frame(vdb_file_name, frame, num_frame_digits); + } + strcat(vdb_file_name, ".vdb"); + + char vdb_file_path[FILE_MAX]; + BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL); + + return vdb_file_path; +} + +std::optional<std::string> USDVolumeWriter::construct_vdb_relative_file_path( + const std::string &vdb_file_path) const +{ + const std::string usd_file_path = get_export_file_path(); + if (usd_file_path.empty()) { + return std::nullopt; + } + + char relative_path[FILE_MAX]; + BLI_strncpy(relative_path, vdb_file_path.c_str(), FILE_MAX); + BLI_path_rel(relative_path, usd_file_path.c_str()); + if (!BLI_path_is_rel(relative_path)) { + return std::nullopt; + } + + /* Following code was written with an assumption that Blender's relative paths start with + * // characters as well as have OS dependent slashes. Inside of USD files those relative + * paths should start with either ./ or ../ characters and have always forward slashes (/) + * separating directories. This is the convention used in USD documentation (and it seems + * to be used in other DCC packages as well). */ + std::string relative_path_processed = pxr::TfNormPath(relative_path + 2); + if (relative_path_processed[0] != '.') { + relative_path_processed.insert(0, "./"); + } + + return relative_path_processed; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_volume.h b/source/blender/io/usd/intern/usd_writer_volume.h new file mode 100644 index 00000000000..8c1e36b7e53 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_volume.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include <optional> + +#include "BLI_math_vec_types.hh" +#include "usd_writer_abstract.h" + +struct Volume; + +namespace blender::io::usd { + +/* Writer for writing OpenVDB assets to UsdVolVolume. Volume data is stored in separate .vdb files + * which are referenced in USD file. */ +class USDVolumeWriter : public USDAbstractWriter { + public: + USDVolumeWriter(const USDExporterContext &ctx); + + protected: + virtual bool check_is_animated(const HierarchyContext &context) const override; + virtual void do_write(HierarchyContext &context) override; + + private: + /* Try to ensure that external .vdb file is available for USD to be referenced. Blender can + * either reference external OpenVDB data or generate such data internally. Latter option will + * mean that `resolve_vdb_file` method will try to export volume data to a new .vdb file. If + * successful, this method returns absolute file path to the resolved .vdb file, if not, returns + * `std::nullopt`. */ + std::optional<std::string> resolve_vdb_file(const Volume *volume) const; + + std::optional<std::string> construct_vdb_file_path(const Volume *volume) const; + std::optional<std::string> construct_vdb_relative_file_path( + const std::string &vdb_file_path) const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index e63cd0a4e04..2e4dcb0da94 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -28,7 +28,7 @@ struct USDExportParams { bool generate_preview_surface; bool export_textures; bool overwrite_textures; - bool relative_texture_paths; + bool relative_paths; }; struct USDImportParams { @@ -83,7 +83,7 @@ int USD_get_version(void); /* USD Import and Mesh Cache interface. */ struct CacheArchiveHandle *USD_create_handle(struct Main *bmain, - const char *filename, + const char *filepath, struct ListBase *object_paths); void USD_free_handle(struct CacheArchiveHandle *handle); diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index 67cec000778..f7958ef4ec6 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../makesrna ../../nodes ../../windowmanager + ../../../../extern/fast_float ../../../../extern/fmtlib/include ../../../../intern/guardedalloc ) @@ -35,6 +36,7 @@ set(SRC importer/obj_import_mesh.cc importer/obj_import_mtl.cc importer/obj_import_nurbs.cc + importer/obj_import_string_utils.cc importer/obj_importer.cc IO_wavefront_obj.h @@ -50,6 +52,7 @@ set(SRC importer/obj_import_mtl.hh importer/obj_import_nurbs.hh importer/obj_import_objects.hh + importer/obj_import_string_utils.hh importer/obj_importer.hh ) @@ -69,8 +72,10 @@ blender_add_lib(bf_wavefront_obj "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/obj_exporter_tests.cc + tests/obj_import_string_utils_tests.cc tests/obj_importer_tests.cc tests/obj_mtl_parser_tests.cc + tests/obj_exporter_tests.hh ) diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index f7bf678310f..bebad06d37f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -92,12 +92,14 @@ struct OBJImportParams { }; /** - * Time the full import process. + * Perform the full import process. + * Import also changes the selection & the active object; callers + * need to update the UI bits if needed. */ void OBJ_import(bContext *C, const struct OBJImportParams *import_params); /** - * C-interface for the exporter. + * Perform the full export process. */ void OBJ_export(bContext *C, const struct OBJExportParams *export_params); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index b027df73b1e..11d1bafdafe 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -385,7 +385,7 @@ void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data) const { - /* Note: ensure_mesh_edges should be called before. */ + /* NOTE: ensure_mesh_edges should be called before. */ const int tot_edges = obj_mesh_data.tot_edges(); for (int edge_index = 0; edge_index < tot_edges; edge_index++) { const std::optional<std::array<int, 2>> vertex_indices = 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 0d7c089ec14..f0263989bfc 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -236,7 +236,7 @@ constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key case eMTLSyntaxElement::Ke: { return {"Ke {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>}; } - /* Note: first texture map related argument, if present, will have its own leading space. */ + /* NOTE: first texture map related argument, if present, will have its own leading space. */ case eMTLSyntaxElement::map_Kd: { return {"map_Kd{} {}\n", 2, is_type_string_related<T...>}; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index d14401224ed..c7990028312 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -8,9 +8,8 @@ #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "IO_string_utils.hh" - #include "obj_import_file_reader.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { @@ -67,40 +66,43 @@ static Geometry *create_geometry(Geometry *const prev_geometry, } static void geom_add_vertex(Geometry *geom, - const StringRef line, + const char *p, + const char *end, GlobalVertices &r_global_vertices) { float3 vert; - parse_floats(line, 0.0f, vert, 3); + parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); geom->vertex_count_++; } static void geom_add_vertex_normal(Geometry *geom, - const StringRef line, + const char *p, + const char *end, GlobalVertices &r_global_vertices) { float3 normal; - parse_floats(line, 0.0f, normal, 3); + parse_floats(p, end, 0.0f, normal, 3); r_global_vertices.vertex_normals.append(normal); geom->has_vertex_normals_ = true; } -static void geom_add_uv_vertex(const StringRef line, GlobalVertices &r_global_vertices) +static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) { float2 uv; - parse_floats(line, 0.0f, uv, 2); + parse_floats(p, end, 0.0f, uv, 2); r_global_vertices.uv_vertices.append(uv); } static void geom_add_edge(Geometry *geom, - StringRef line, + const char *p, + const char *end, const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { int edge_v1, edge_v2; - line = parse_int(line, -1, edge_v1); - line = parse_int(line, -1, edge_v2); + p = parse_int(p, end, -1, edge_v1); + p = parse_int(p, end, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; @@ -109,7 +111,8 @@ static void geom_add_edge(Geometry *geom, } static void geom_add_polygon(Geometry *geom, - StringRef line, + const char *p, + const char *end, const GlobalVertices &global_vertices, const VertexIndexOffset &offsets, const int material_index, @@ -128,24 +131,24 @@ static void geom_add_polygon(Geometry *geom, curr_face.start_index_ = orig_corners_size; bool face_valid = true; - line = drop_whitespace(line); - while (!line.is_empty() && face_valid) { + p = drop_whitespace(p, end); + while (p < end && face_valid) { PolyCorner corner; bool got_uv = false, got_normal = false; /* Parse vertex index. */ - line = parse_int(line, INT32_MAX, corner.vert_index, false); + p = parse_int(p, end, INT32_MAX, corner.vert_index, false); face_valid &= corner.vert_index != INT32_MAX; - if (!line.is_empty() && line[0] == '/') { + if (p < end && *p == '/') { /* Parse UV index. */ - line = line.drop_prefix(1); - if (!line.is_empty() && line[0] != '/') { - line = parse_int(line, INT32_MAX, corner.uv_vert_index, false); + ++p; + if (p < end && *p != '/') { + p = parse_int(p, end, INT32_MAX, corner.uv_vert_index, false); got_uv = corner.uv_vert_index != INT32_MAX; } /* Parse normal index. */ - if (!line.is_empty() && line[0] == '/') { - line = line.drop_prefix(1); - line = parse_int(line, INT32_MAX, corner.vertex_normal_index, false); + if (p < end && *p == '/') { + ++p; + p = parse_int(p, end, INT32_MAX, corner.vertex_normal_index, false); got_normal = corner.uv_vert_index != INT32_MAX; } } @@ -186,7 +189,7 @@ static void geom_add_polygon(Geometry *geom, curr_face.corner_count_++; /* Skip whitespace to get to the next face corner. */ - line = drop_whitespace(line); + p = drop_whitespace(p, end); } if (face_valid) { @@ -201,14 +204,16 @@ static void geom_add_polygon(Geometry *geom, } static Geometry *geom_set_curve_type(Geometry *geom, - const StringRef rest_line, + const char *p, + const char *end, const GlobalVertices &global_vertices, const StringRef group_name, VertexIndexOffset &r_offsets, Vector<std::unique_ptr<Geometry>> &r_all_geometries) { - if (rest_line.find("bspline") == StringRef::not_found) { - std::cerr << "Curve type not supported:'" << rest_line << "'" << std::endl; + p = drop_whitespace(p, end); + if (!StringRef(p, end).startswith("bspline")) { + std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl; return geom; } geom = create_geometry( @@ -217,22 +222,23 @@ static Geometry *geom_set_curve_type(Geometry *geom, return geom; } -static void geom_set_curve_degree(Geometry *geom, const StringRef line) +static void geom_set_curve_degree(Geometry *geom, const char *p, const char *end) { - parse_int(line, 3, geom->nurbs_element_.degree); + parse_int(p, end, 3, geom->nurbs_element_.degree); } static void geom_add_curve_vertex_indices(Geometry *geom, - StringRef line, + const char *p, + const char *end, const GlobalVertices &global_vertices) { /* Curve lines always have "0.0" and "1.0", skip over them. */ float dummy[2]; - line = parse_floats(line, 0, dummy, 2); + p = parse_floats(p, end, 0, dummy, 2); /* Parse indices. */ - while (!line.is_empty()) { + while (p < end) { int index; - line = parse_int(line, INT32_MAX, index); + p = parse_int(p, end, INT32_MAX, index); if (index == INT32_MAX) { return; } @@ -242,22 +248,22 @@ static void geom_add_curve_vertex_indices(Geometry *geom, } } -static void geom_add_curve_parameters(Geometry *geom, StringRef line) +static void geom_add_curve_parameters(Geometry *geom, const char *p, const char *end) { - line = drop_whitespace(line); - if (line.is_empty()) { - std::cerr << "Invalid OBJ curve parm line: '" << line << "'" << std::endl; + p = drop_whitespace(p, end); + if (p == end) { + std::cerr << "Invalid OBJ curve parm line" << std::endl; return; } - if (line[0] != 'u') { - std::cerr << "OBJ curve surfaces are not supported: '" << line[0] << "'" << std::endl; + if (*p != 'u') { + std::cerr << "OBJ curve surfaces are not supported: '" << *p << "'" << std::endl; return; } - line = line.drop_prefix(1); + ++p; - while (!line.is_empty()) { + while (p < end) { float val; - line = parse_float(line, FLT_MAX, val); + p = parse_float(p, end, FLT_MAX, val); if (val != FLT_MAX) { geom->nurbs_element_.parm.append(val); } @@ -270,7 +276,6 @@ static void geom_add_curve_parameters(Geometry *geom, StringRef line) static void geom_update_group(const StringRef rest_line, std::string &r_group_name) { - if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos || rest_line.find("default") != string::npos) { /* Set group for future elements like faces or curves to empty. */ @@ -280,17 +285,18 @@ static void geom_update_group(const StringRef rest_line, std::string &r_group_na r_group_name = rest_line; } -static void geom_update_smooth_group(StringRef line, bool &r_state_shaded_smooth) +static void geom_update_smooth_group(const char *p, const char *end, bool &r_state_shaded_smooth) { - line = drop_whitespace(line); + p = drop_whitespace(p, end); /* Some implementations use "0" and "null" too, in addition to "off". */ + const StringRef line = StringRef(p, end); if (line == "0" || line.startswith("off") || line.startswith("null")) { r_state_shaded_smooth = false; return; } int smooth = 0; - parse_int(line, 0, smooth); + parse_int(p, end, 0, smooth); r_state_shaded_smooth = smooth != 0; } @@ -312,21 +318,21 @@ OBJParser::~OBJParser() } /* If line starts with keyword followed by whitespace, returns true and drops it from the line. */ -static bool parse_keyword(StringRef &line, StringRef keyword) +static bool parse_keyword(const char *&p, const char *end, StringRef keyword) { const size_t keyword_len = keyword.size(); - if (line.size() < keyword_len + 1) { + if (end - p < keyword_len + 1) { return false; } - if (!line.startswith(keyword)) { + if (memcmp(p, keyword.data(), keyword_len) != 0) { return false; } - /* Treat any ASCII control character as whitespace; don't use isspace() for performance reasons. - */ - if (line[keyword_len] > ' ') { + /* Treat any ASCII control character as white-space; + * don't use `isspace()` for performance reasons. */ + if (p[keyword_len] > ' ') { return false; } - line = line.drop_prefix(keyword_len + 1); + p += keyword_len + 1; return true; } @@ -400,27 +406,29 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, StringRef buffer_str{buffer.data(), (int64_t)last_nl}; while (!buffer_str.is_empty()) { StringRef line = read_next_line(buffer_str); - line = drop_whitespace(line); + const char *p = line.begin(), *end = line.end(); + p = drop_whitespace(p, end); ++line_number; - if (line.is_empty()) { + if (p == end) { continue; } /* Most common things that start with 'v': vertices, normals, UVs. */ - if (line[0] == 'v') { - if (parse_keyword(line, "v")) { - geom_add_vertex(curr_geom, line, r_global_vertices); + if (*p == 'v') { + if (parse_keyword(p, end, "v")) { + geom_add_vertex(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "vn")) { - geom_add_vertex_normal(curr_geom, line, r_global_vertices); + else if (parse_keyword(p, end, "vn")) { + geom_add_vertex_normal(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "vt")) { - geom_add_uv_vertex(line, r_global_vertices); + else if (parse_keyword(p, end, "vt")) { + geom_add_uv_vertex(p, end, r_global_vertices); } } /* Faces. */ - else if (parse_keyword(line, "f")) { + else if (parse_keyword(p, end, "f")) { geom_add_polygon(curr_geom, - line, + p, + end, r_global_vertices, offsets, state_material_index, @@ -428,20 +436,24 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, state_shaded_smooth); } /* Faces. */ - else if (parse_keyword(line, "l")) { - geom_add_edge(curr_geom, line, offsets, r_global_vertices); + else if (parse_keyword(p, end, "l")) { + geom_add_edge(curr_geom, p, end, offsets, r_global_vertices); } /* Objects. */ - else if (parse_keyword(line, "o")) { + else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; state_material_name = ""; - curr_geom = create_geometry( - curr_geom, GEOM_MESH, line.trim(), r_global_vertices, r_all_geometries, offsets); + curr_geom = create_geometry(curr_geom, + GEOM_MESH, + StringRef(p, end).trim(), + r_global_vertices, + r_all_geometries, + offsets); } /* Groups. */ - else if (parse_keyword(line, "g")) { - geom_update_group(line.trim(), state_group_name); + else if (parse_keyword(p, end, "g")) { + geom_update_group(StringRef(p, end).trim(), state_group_name); int new_index = curr_geom->group_indices_.size(); state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index); if (new_index == state_group_index) { @@ -449,12 +461,12 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, } } /* Smoothing groups. */ - else if (parse_keyword(line, "s")) { - geom_update_smooth_group(line, state_shaded_smooth); + else if (parse_keyword(p, end, "s")) { + geom_update_smooth_group(p, end, state_shaded_smooth); } /* Materials and their libraries. */ - else if (parse_keyword(line, "usemtl")) { - state_material_name = line.trim(); + else if (parse_keyword(p, end, "usemtl")) { + state_material_name = StringRef(p, end).trim(); int new_mat_index = curr_geom->material_indices_.size(); state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name, new_mat_index); @@ -462,32 +474,32 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, curr_geom->material_order_.append(state_material_name); } } - else if (parse_keyword(line, "mtllib")) { - add_mtl_library(line.trim()); + else if (parse_keyword(p, end, "mtllib")) { + add_mtl_library(StringRef(p, end).trim()); } /* Comments. */ - else if (line.startswith("#")) { + else if (*p == '#') { /* Nothing to do. */ } /* Curve related things. */ - else if (parse_keyword(line, "cstype")) { + else if (parse_keyword(p, end, "cstype")) { curr_geom = geom_set_curve_type( - curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries); + curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries); } - else if (parse_keyword(line, "deg")) { - geom_set_curve_degree(curr_geom, line); + else if (parse_keyword(p, end, "deg")) { + geom_set_curve_degree(curr_geom, p, end); } - else if (parse_keyword(line, "curv")) { - geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices); + else if (parse_keyword(p, end, "curv")) { + geom_add_curve_vertex_indices(curr_geom, p, end, r_global_vertices); } - else if (parse_keyword(line, "parm")) { - geom_add_curve_parameters(curr_geom, line); + else if (parse_keyword(p, end, "parm")) { + geom_add_curve_parameters(curr_geom, p, end); } - else if (line.startswith("end")) { + else if (StringRef(p, end).startswith("end")) { /* End of curve definition, nothing else to do. */ } else { - std::cout << "OBJ element not recognized: '" << line << "'" << std::endl; + std::cout << "OBJ element not recognized: '" << std::string(p, end) << "'" << std::endl; } } @@ -501,33 +513,33 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, add_default_mtl_library(); } -static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line) +static eMTLSyntaxElement mtl_line_start_to_enum(const char *&p, const char *end) { - if (parse_keyword(line, "map_Kd")) { + if (parse_keyword(p, end, "map_Kd")) { return eMTLSyntaxElement::map_Kd; } - if (parse_keyword(line, "map_Ks")) { + if (parse_keyword(p, end, "map_Ks")) { return eMTLSyntaxElement::map_Ks; } - if (parse_keyword(line, "map_Ns")) { + if (parse_keyword(p, end, "map_Ns")) { return eMTLSyntaxElement::map_Ns; } - if (parse_keyword(line, "map_d")) { + if (parse_keyword(p, end, "map_d")) { return eMTLSyntaxElement::map_d; } - if (parse_keyword(line, "refl")) { + if (parse_keyword(p, end, "refl")) { return eMTLSyntaxElement::map_refl; } - if (parse_keyword(line, "map_refl")) { + if (parse_keyword(p, end, "map_refl")) { return eMTLSyntaxElement::map_refl; } - if (parse_keyword(line, "map_Ke")) { + if (parse_keyword(p, end, "map_Ke")) { return eMTLSyntaxElement::map_Ke; } - if (parse_keyword(line, "bump")) { + if (parse_keyword(p, end, "bump")) { return eMTLSyntaxElement::map_Bump; } - if (parse_keyword(line, "map_Bump") || parse_keyword(line, "map_bump")) { + if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) { return eMTLSyntaxElement::map_Bump; } return eMTLSyntaxElement::string; @@ -545,39 +557,43 @@ static const std::pair<StringRef, int> unsupported_texture_options[] = { {"-texres", 1}, }; -static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map) +static bool parse_texture_option(const char *&p, + const char *end, + MTLMaterial *material, + tex_map_XX &tex_map) { - line = drop_whitespace(line); - if (parse_keyword(line, "-o")) { - line = parse_floats(line, 0.0f, tex_map.translation, 3); + p = drop_whitespace(p, end); + if (parse_keyword(p, end, "-o")) { + p = parse_floats(p, end, 0.0f, tex_map.translation, 3); return true; } - if (parse_keyword(line, "-s")) { - line = parse_floats(line, 1.0f, tex_map.scale, 3); + if (parse_keyword(p, end, "-s")) { + p = parse_floats(p, end, 1.0f, tex_map.scale, 3); return true; } - if (parse_keyword(line, "-bm")) { - line = parse_float(line, 1.0f, material->map_Bump_strength); + if (parse_keyword(p, end, "-bm")) { + p = parse_float(p, end, 1.0f, material->map_Bump_strength); return true; } - if (parse_keyword(line, "-type")) { - line = drop_whitespace(line); + if (parse_keyword(p, end, "-type")) { + p = drop_whitespace(p, end); /* Only sphere is supported. */ tex_map.projection_type = SHD_PROJ_SPHERE; + const StringRef line = StringRef(p, end); if (!line.startswith("sphere")) { std::cerr << "OBJ import: only sphere MTL projection type is supported: '" << line << "'" << std::endl; } - line = drop_non_whitespace(line); + p = drop_non_whitespace(p, end); return true; } /* Check for unsupported options and skip them. */ for (const auto &opt : unsupported_texture_options) { - if (parse_keyword(line, opt.first)) { + if (parse_keyword(p, end, opt.first)) { /* Drop the arguments. */ for (int i = 0; i < opt.second; ++i) { - line = drop_whitespace(line); - line = drop_non_whitespace(line); + p = drop_whitespace(p, end); + p = drop_non_whitespace(p, end); } return true; } @@ -586,15 +602,19 @@ static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map return false; } -static void parse_texture_map(StringRef line, MTLMaterial *material, const char *mtl_dir_path) +static void parse_texture_map(const char *p, + const char *end, + MTLMaterial *material, + const char *mtl_dir_path) { + const StringRef line = StringRef(p, end); bool is_map = line.startswith("map_"); bool is_refl = line.startswith("refl"); bool is_bump = line.startswith("bump"); if (!is_map && !is_refl && !is_bump) { return; } - eMTLSyntaxElement key = mtl_line_start_to_enum(line); + eMTLSyntaxElement key = mtl_line_start_to_enum(p, end); if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) { /* No supported texture map found. */ std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl; @@ -604,12 +624,11 @@ static void parse_texture_map(StringRef line, MTLMaterial *material, const char tex_map.mtl_dir_path = mtl_dir_path; /* Parse texture map options. */ - while (parse_texture_option(line, material, tex_map)) { + while (parse_texture_option(p, end, material, tex_map)) { } /* What remains is the image path. */ - line = line.trim(); - tex_map.image_path = line; + tex_map.image_path = StringRef(p, end).trim(); } Span<std::string> OBJParser::mtl_libraries() const @@ -667,51 +686,53 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len}; while (!buffer_str.is_empty()) { - StringRef line = read_next_line(buffer_str); - line = drop_whitespace(line); - if (line.is_empty()) { + const StringRef line = read_next_line(buffer_str); + const char *p = line.begin(), *end = line.end(); + p = drop_whitespace(p, end); + if (p == end) { continue; } - if (parse_keyword(line, "newmtl")) { - line = line.trim(); - if (r_materials.contains(line)) { + if (parse_keyword(p, end, "newmtl")) { + StringRef mat_name = StringRef(p, end).trim(); + if (r_materials.contains(mat_name)) { material = nullptr; } else { - material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get(); + material = + r_materials.lookup_or_add(string(mat_name), std::make_unique<MTLMaterial>()).get(); } } else if (material != nullptr) { - if (parse_keyword(line, "Ns")) { - parse_float(line, 324.0f, material->Ns); + if (parse_keyword(p, end, "Ns")) { + parse_float(p, end, 324.0f, material->Ns); } - else if (parse_keyword(line, "Ka")) { - parse_floats(line, 0.0f, material->Ka, 3); + else if (parse_keyword(p, end, "Ka")) { + parse_floats(p, end, 0.0f, material->Ka, 3); } - else if (parse_keyword(line, "Kd")) { - parse_floats(line, 0.8f, material->Kd, 3); + else if (parse_keyword(p, end, "Kd")) { + parse_floats(p, end, 0.8f, material->Kd, 3); } - else if (parse_keyword(line, "Ks")) { - parse_floats(line, 0.5f, material->Ks, 3); + else if (parse_keyword(p, end, "Ks")) { + parse_floats(p, end, 0.5f, material->Ks, 3); } - else if (parse_keyword(line, "Ke")) { - parse_floats(line, 0.0f, material->Ke, 3); + else if (parse_keyword(p, end, "Ke")) { + parse_floats(p, end, 0.0f, material->Ke, 3); } - else if (parse_keyword(line, "Ni")) { - parse_float(line, 1.45f, material->Ni); + else if (parse_keyword(p, end, "Ni")) { + parse_float(p, end, 1.45f, material->Ni); } - else if (parse_keyword(line, "d")) { - parse_float(line, 1.0f, material->d); + else if (parse_keyword(p, end, "d")) { + parse_float(p, end, 1.0f, material->d); } - else if (parse_keyword(line, "illum")) { + else if (parse_keyword(p, end, "illum")) { /* Some files incorrectly use a float (T60135). */ float val; - parse_float(line, 1.0f, val); + parse_float(p, end, 1.0f, val); material->illum = val; } else { - parse_texture_map(line, material, mtl_dir_path_); + parse_texture_map(p, end, material, mtl_dir_path_); } } } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh index e41a7f8518e..8bfc5fe8bf0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh @@ -13,7 +13,7 @@ namespace blender::io::obj { -/* Note: the OBJ parser implementation is planned to get fairly large changes "soon", +/* NOTE: the OBJ parser implementation is planned to get fairly large changes "soon", * so don't read too much into current implementation... */ class OBJParser { private: diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index c2ecd8a37de..f39def0a4af 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -13,13 +13,12 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" -#include "IO_string_utils.hh" - #include "NOD_shader.h" /* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ #include "obj_export_io.hh" #include "obj_import_mtl.hh" +#include "obj_import_string_utils.hh" namespace blender::io::obj { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc new file mode 100644 index 00000000000..c8eaa046e68 --- /dev/null +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "obj_import_string_utils.hh" + +/* NOTE: we could use C++17 <charconv> from_chars to parse + * floats, but even if some compilers claim full support, + * their standard libraries are not quite there yet. + * LLVM/libc++ only has a float parser since LLVM 14, + * and gcc/libstdc++ since 11.1. So until at least these are + * the minimum spec, use an external library. */ +#include "fast_float.h" +#include <charconv> + +namespace blender::io::obj { + +StringRef read_next_line(StringRef &buffer) +{ + const char *start = buffer.begin(); + const char *end = buffer.end(); + size_t len = 0; + char prev = 0; + const char *ptr = start; + while (ptr < end) { + char c = *ptr++; + if (c == '\n' && prev != '\\') { + break; + } + prev = c; + ++len; + } + + buffer = StringRef(ptr, end); + return StringRef(start, len); +} + +static bool is_whitespace(char c) +{ + return c <= ' ' || c == '\\'; +} + +const char *drop_whitespace(const char *p, const char *end) +{ + while (p < end && is_whitespace(*p)) { + ++p; + } + return p; +} + +const char *drop_non_whitespace(const char *p, const char *end) +{ + while (p < end && !is_whitespace(*p)) { + ++p; + } + return p; +} + +static const char *drop_plus(const char *p, const char *end) +{ + if (p < end && *p == '+') { + ++p; + } + return p; +} + +const char *parse_float( + const char *p, const char *end, float fallback, float &dst, bool skip_space) +{ + if (skip_space) { + p = drop_whitespace(p, end); + } + p = drop_plus(p, end); + fast_float::from_chars_result res = fast_float::from_chars(p, end, dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return res.ptr; +} + +const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count) +{ + for (int i = 0; i < count; ++i) { + p = parse_float(p, end, fallback, dst[i]); + } + return p; +} + +const char *parse_int(const char *p, const char *end, int fallback, int &dst, bool skip_space) +{ + if (skip_space) { + p = drop_whitespace(p, end); + } + p = drop_plus(p, end); + std::from_chars_result res = std::from_chars(p, end, dst); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + dst = fallback; + } + return res.ptr; +} + +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh new file mode 100644 index 00000000000..3f428b1ab5c --- /dev/null +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +/* + * Various text parsing utilities used by OBJ importer. + * The utilities are not directly usable by other formats, since + * they treat backslash (\) as a whitespace character (OBJ format + * allows backslashes to function as a line-continuation character). + * + * Many of these functions take two pointers (p, end) indicating + * which part of a string to operate on, and return a possibly + * changed new start of the string. They could be taking a StringRef + * as input and returning a new StringRef, but this is a hot path + * in OBJ parsing, and the StringRef approach does lose performance + * (mostly due to return of StringRef being two register-size values + * instead of just one pointer). + */ + +namespace blender::io::obj { + +/** + * Fetches next line from an input string buffer. + * + * The returned line will not have '\n' characters at the end; + * the `buffer` is modified to contain remaining text without + * the input line. + * + * Note that backslash (\) character is treated as a line + * continuation. + */ +StringRef read_next_line(StringRef &buffer); + +/** + * Drop leading white-space from a string part. + * Note that backslash character is considered white-space. + */ +const char *drop_whitespace(const char *p, const char *end); + +/** + * Drop leading non-white-space from a string part. + * Note that backslash character is considered white-space. + */ +const char *drop_non_whitespace(const char *p, const char *end); + +/** + * Parse an integer from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_int( + const char *p, const char *end, int fallback, int &dst, bool skip_space = true); + +/** + * Parse a float from an input string. + * The parsed result is stored in `dst`. The function skips + * leading white-space unless `skip_space=false`. If the + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_float( + const char *p, const char *end, float fallback, float &dst, bool skip_space = true); + +/** + * Parse a number of white-space separated floats from an input string. + * The parsed `count` numbers are stored in `dst`. If a + * number can't be parsed (invalid syntax, out of range), + * `fallback` value is stored instead. + * + * Returns the start of remainder of the input string after parsing. + */ +const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count); + +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index b18ff2cf454..f2051d195c8 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -88,7 +88,6 @@ void importer_main(bContext *C, const OBJImportParams &import_params) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); importer_main(bmain, scene, view_layer, import_params); - static_cast<void>(CTX_data_ensure_evaluated_depsgraph(C)); } void importer_main(Main *bmain, diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index a78bd7ab8a3..46e093bb8a7 100644 --- a/source/blender/io/common/intern/string_utils_test.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -1,14 +1,14 @@ /* SPDX-License-Identifier: Apache-2.0 */ -#include "IO_string_utils.hh" +#include "obj_import_string_utils.hh" #include "testing/testing.h" -namespace blender::io { +namespace blender::io::obj { #define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str()) -TEST(string_utils, read_next_line) +TEST(obj_import_string_utils, read_next_line) { std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; StringRef s = str; @@ -21,7 +21,20 @@ TEST(string_utils, read_next_line) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, drop_whitespace) +static StringRef drop_whitespace(StringRef s) +{ + return StringRef(drop_whitespace(s.begin(), s.end()), s.end()); +} +static StringRef parse_int(StringRef s, int fallback, int &dst, bool skip_space = true) +{ + return StringRef(parse_int(s.begin(), s.end(), fallback, dst, skip_space), s.end()); +} +static StringRef parse_float(StringRef s, float fallback, float &dst, bool skip_space = true) +{ + return StringRef(parse_float(s.begin(), s.end(), fallback, dst, skip_space), s.end()); +} + +TEST(obj_import_string_utils, drop_whitespace) { /* Empty */ EXPECT_STRREF_EQ("", drop_whitespace("")); @@ -39,7 +52,7 @@ TEST(string_utils, drop_whitespace) EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); } -TEST(string_utils, parse_int_valid) +TEST(obj_import_string_utils, parse_int_valid) { std::string str = "1 -10 \t 1234 1234567890 +7 123a"; StringRef s = str; @@ -59,7 +72,7 @@ TEST(string_utils, parse_int_valid) EXPECT_STRREF_EQ("a", s); } -TEST(string_utils, parse_int_invalid) +TEST(obj_import_string_utils, parse_int_invalid) { int val; /* Invalid syntax */ @@ -75,7 +88,7 @@ TEST(string_utils, parse_int_invalid) EXPECT_EQ(val, -4); } -TEST(string_utils, parse_float_valid) +TEST(obj_import_string_utils, parse_float_valid) { std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1"; StringRef s = str; @@ -97,7 +110,7 @@ TEST(string_utils, parse_float_valid) EXPECT_TRUE(s.is_empty()); } -TEST(string_utils, parse_float_invalid) +TEST(obj_import_string_utils, parse_float_invalid) { float val; /* Invalid syntax */ @@ -115,4 +128,4 @@ TEST(string_utils, parse_float_invalid) EXPECT_EQ(val, -4.0f); } -} // namespace blender::io +} // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 3d34fb6f9c6..d7f4ce3d773 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -333,7 +333,7 @@ TEST_F(obj_importer_test, import_invalid_syntax) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon", OB_MESH, - 10, /* Note: right now parses some invalid obj syntax as valid vertices. */ + 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */ 3, 1, 3, diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index 99737aa3b67..c1dfab8a041 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -304,7 +304,7 @@ typedef struct DriverTarget { char *rna_path; /** - * Name of the posebone to use + * Name of the pose-bone to use * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`. */ char pchan_name[64]; @@ -918,9 +918,9 @@ typedef struct KS_Path { /** Index that path affects. */ int array_index; - /** (eInsertKeyFlags) settings to supply insertkey() with. */ + /** (#eInsertKeyFlags) settings to supply insert-key() with. */ short keyingflag; - /** (eInsertKeyFlags) for each flag set, the relevant keyingflag bit overrides the default. */ + /** (#eInsertKeyFlags) for each flag set, the relevant keying-flag bit overrides the default. */ short keyingoverride; } KS_Path; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 3e7a4431bf5..f2cd49b6dea 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -305,6 +305,12 @@ typedef enum eGP_Sculpt_Mode_Flag { GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), /* apply brush to uv data */ GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), + /* Stroke Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_STROKE = (1 << 4), + /* Layer Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_LAYER = (1 << 5), + /* Material Auto-Masking for sculpt. */ + GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL = (1 << 6), } eGP_Sculpt_Mode_Flag; typedef enum eAutomasking_flag { @@ -462,6 +468,7 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_SNAKE_HOOK = 2, CURVES_SCULPT_TOOL_ADD = 3, CURVES_SCULPT_TOOL_GROW_SHRINK = 4, + CURVES_SCULPT_TOOL_SELECTION_PAINT = 5, } eBrushCurvesSculptTool; /** When #BRUSH_ACCUMULATE is used */ @@ -612,6 +619,7 @@ typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2), BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3), + BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4), } eBrushCurvesSculptFlag; #define MAX_BRUSH_PIXEL_RADIUS 500 diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7d230b7d7a3..24e77ecf87f 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -140,6 +140,8 @@ typedef struct BrushGpencilSettings { typedef struct BrushCurvesSculptSettings { /** Number of curves added by the add brush. */ int add_amount; + /** Number of control points in new curves added by the add brush. */ + int points_per_curve; /* eBrushCurvesSculptFlag. */ uint32_t flag; /** When shrinking curves, they shouldn't become shorter than this length. */ diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index 9b3adc4c8dd..e0aec298cd0 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -194,6 +194,9 @@ enum { /* Axis flip options */ CAM_BGIMG_FLAG_FLIP_X = (1 << 7), CAM_BGIMG_FLAG_FLIP_Y = (1 << 8), + + /* That background image has been inserted in local override (i.e. it can be fully edited!). */ + CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL = (1 << 9), }; /* CameraBGImage->source */ diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index bb53dbafdc8..45b2309c5bf 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -9,6 +9,8 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -87,6 +89,9 @@ typedef struct CurvesGeometry { * this array is allocated with a length one larger than the number of curves. This is allowed * to be null when there are no curves. * + * Every curve offset must be at least one larger than the previous. + * In other words, every curve must have at least one point. + * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ int *curve_offsets; @@ -105,11 +110,11 @@ typedef struct CurvesGeometry { /** * The total number of control points in all curves. */ - int point_size; + int point_num; /** * The number of curves in the data-block. */ - int curve_size; + int curve_num; /** * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. @@ -130,7 +135,18 @@ typedef struct Curves { /* Materials. */ struct Material **mat; short totcol; - short _pad2[3]; + + /** + * User-defined symmetry flag (#eCurvesSymmetryType) that causes editing operations to maintain + * symmetrical geometry. + */ + char symmetry; + /** + * #AttributeDomain. The active selection mode domain. At most one selection mode can be active + * at a time. + */ + char selection_domain; + char _pad[4]; /** * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes. @@ -148,8 +164,17 @@ typedef struct Curves { /** #Curves.flag */ enum { HA_DS_EXPAND = (1 << 0), + CV_SCULPT_SELECTION_ENABLED = (1 << 1), }; +/** #Curves.symmetry */ +typedef enum eCurvesSymmetryType { + CURVES_SYMMETRY_X = 1 << 0, + CURVES_SYMMETRY_Y = 1 << 1, + CURVES_SYMMETRY_Z = 1 << 2, +} eCurvesSymmetryType; +ENUM_OPERATORS(eCurvesSymmetryType, CURVES_SYMMETRY_Z) + /* Only one material supported currently. */ #define CURVES_MATERIAL_NR 1 diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index ef937fb139b..fe06e97946c 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -260,8 +260,6 @@ enum { #define DYNTOPO_NODE_NONE -1 -#define CD_TEMP_CHUNK_SIZE 128 - #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 750f97bb3c6..ae47bf5d524 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -303,7 +303,8 @@ .crease_threshold = DEG2RAD(140.0f), \ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \ LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR, \ - .angle_splitting_threshold = DEG2RAD(60.0f), \ + /* Do not split by default, this is for better chaining quality. */ \ + .angle_splitting_threshold = 0.0f, \ .chaining_image_threshold = 0.001f, \ .chain_smooth_tolerance = 0.2f,\ .stroke_depth_offset = 0.05,\ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index b0e7342c9cb..c20fb180fcd 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -102,7 +102,8 @@ typedef struct NoiseGpencilModifierData { /** Noise Frequency scaling */ float noise_scale; float noise_offset; - char _pad[4]; + short noise_mode; + char _pad[2]; /** How many frames before recalculate randoms. */ int step; /** Custom index for passes. */ @@ -127,6 +128,11 @@ typedef enum eNoiseGpencil_Flag { GP_NOISE_INVERT_MATERIAL = (1 << 11), } eNoiseGpencil_Flag; +typedef enum eNoiseRandomGpencil_Mode { + GP_NOISE_RANDOM_STEP = 0, + GP_NOISE_RANDOM_KEYFRAME = 1, +} eNoiseRandomGpencil_Mode; + typedef struct SubdivGpencilModifierData { GpencilModifierData modifier; /** Material for filtering. */ @@ -223,6 +229,7 @@ typedef enum eTimeGpencil_Mode { GP_TIME_MODE_NORMAL = 0, GP_TIME_MODE_REVERSE = 1, GP_TIME_MODE_FIX = 2, + GP_TIME_MODE_PINGPONG = 3, } eTimeGpencil_Mode; typedef enum eModifyColorGpencil_Flag { diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 4a1b639122a..a83262d7639 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -245,7 +245,7 @@ typedef struct bGPDstroke_Runtime { /** Vertex offset in the VBO where this stroke starts. */ int stroke_start; - /** Triangle offset in the ibo where this fill starts. */ + /** Triangle offset in the IBO where this fill starts. */ int fill_start; /** Curve Handles offset in the IBO where this handle starts. */ int curve_start; @@ -814,10 +814,10 @@ typedef enum eGPdata_Flag { /* Vertex Paint Mode - Toggle paint mode */ GP_DATA_STROKE_VERTEXMODE = (1 << 18), - /* Autolock not active layers */ + /* Auto-lock not active layers. */ GP_DATA_AUTOLOCK_LAYERS = (1 << 20), - /* Enable Bezier Editing Curve (a submode of Edit mode). */ + /* Enable Bezier Editing Curve (a sub-mode of Edit mode). */ GP_DATA_CURVE_EDIT_MODE = (1 << 21), /* Use adaptive curve resolution */ GP_DATA_CURVE_ADAPTIVE_RESOLUTION = (1 << 22), diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 4e66e2446f0..6e4e515a0fe 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -64,6 +64,11 @@ typedef struct ImageView { typedef struct ImagePackedFile { struct ImagePackedFile *next, *prev; struct PackedFile *packedfile; + + /* Which view and tile this ImagePackedFile represents. Normal images will use 0 and 1001 + * respectively when creating their ImagePackedFile. Must be provided for each packed image. */ + int view; + int tile_number; /** 1024 = FILE_MAX. */ char filepath[1024]; } ImagePackedFile; @@ -75,17 +80,11 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; -typedef struct ImageTile_RuntimeTextureSlot { +typedef struct ImageTile_Runtime { int tilearray_layer; int _pad; int tilearray_offset[2]; int tilearray_size[2]; -} ImageTile_RuntimeTextureSlot; - -typedef struct ImageTile_Runtime { - /* Data per `eImageTextureResolution`. - * Should match `IMA_TEXTURE_RESOLUTION_LEN` */ - ImageTile_RuntimeTextureSlot slots[2]; } ImageTile_Runtime; typedef struct ImageTile { @@ -104,10 +103,7 @@ typedef struct ImageTile { /* #define IMA_UNUSED_2 (1 << 2) */ #define IMA_NEED_FRAME_RECALC (1 << 3) #define IMA_SHOW_STEREO (1 << 4) -/* Do not limit the resolution by the limit texture size option in the user preferences. - * Images in the image editor or used as a backdrop are always shown using the maximum - * possible resolution. */ -#define IMA_SHOW_MAX_RESOLUTION (1 << 5) +/* #define IMA_UNUSED_5 (1 << 5) */ /* Used to get the correct gpu texture from an Image datablock. */ typedef enum eGPUTextureTarget { @@ -117,15 +113,6 @@ typedef enum eGPUTextureTarget { TEXTARGET_COUNT, } eGPUTextureTarget; -/* Resolution variations that can be cached for an image. */ -typedef enum eImageTextureResolution { - IMA_TEXTURE_RESOLUTION_FULL = 0, - IMA_TEXTURE_RESOLUTION_LIMITED, - - /* Not an option, but holds the number of options defined for this struct. */ - IMA_TEXTURE_RESOLUTION_LEN -} eImageTextureResolution; - /* Defined in BKE_image.h. */ struct PartialUpdateRegister; struct PartialUpdateUser; @@ -150,8 +137,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */ - struct GPUTexture *gputexture[3][2][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ + struct GPUTexture *gputexture[3][2]; /* sources from: */ ListBase anims; @@ -239,11 +226,6 @@ enum { enum { /** All mipmap levels in OpenGL texture set? */ IMA_GPU_MIPMAP_COMPLETE = (1 << 0), - /* Reuse the max resolution textures as they fit in the limited scale. */ - IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 1), - /* Has any limited scale textures been allocated. - * Adds additional checks to reuse max resolution images when they fit inside limited scale. */ - IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 2), }; /* Image.source, where the image comes from */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index d5f7e25bb80..4ee5f34fcde 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -125,7 +125,7 @@ typedef struct ViewLayerAOV { int type; } ViewLayerAOV; -/* Lightgroup Renderpass definition. */ +/** Light-group Render-pass definition. */ typedef struct ViewLayerLightgroup { struct ViewLayerLightgroup *next, *prev; diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index 2e446427cc3..5b3a23000d7 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -21,7 +21,7 @@ typedef enum eLineartMainFlags { LRT_ALLOW_DUPLI_OBJECTS = (1 << 2), LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3), LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4), - LRT_REMOVE_DOUBLES = (1 << 5), + /* LRT_REMOVE_DOUBLES = (1 << 5), Deprecated */ LRT_LOOSE_AS_CONTOUR = (1 << 6), LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 7), LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 8), @@ -47,9 +47,18 @@ typedef enum eLineartEdgeFlag { LRT_EDGE_FLAG_MATERIAL = (1 << 3), LRT_EDGE_FLAG_INTERSECTION = (1 << 4), LRT_EDGE_FLAG_LOOSE = (1 << 5), - LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 6), - LRT_EDGE_FLAG_CLIPPED = (1 << 7), - /** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */ + /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ + /* Limited to 8 bits for edge type flag, don't add anymore because BMEdge->head.eflag only has 8 + bits. So unless we changed this into a non-single-bit flag thing, we keep it this way. */ + /** Also used as discarded line mark. */ + LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8), + LRT_EDGE_FLAG_CLIPPED = (1 << 9), + /** Limited to 16 bits for the entire thing. */ + + /** For object loading code to use only. */ + LRT_EDGE_FLAG_INHIBIT = (1 << 14), + /** For discarding duplicated edge types in culling stage. */ + LRT_EDGE_FLAG_NEXT_IS_DUPLICATION = (1 << 15), } eLineartEdgeFlag; #define LRT_EDGE_FLAG_ALL_TYPE 0x3f diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 77730ce254c..92a65a50bd4 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -632,7 +632,8 @@ .falloff = 4.0f, \ .mesh_verts_num = 0, \ .bind_verts_num = 0, \ - .polys_num = 0, \ + .target_verts_num = 0, \ + .target_polys_num = 0, \ .flags = 0, \ .mat = _DNA_DEFAULT_UNIT_M4, \ .strength = 1.0f, \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 73c4eeaaab3..6e3ce7e98a8 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2222,13 +2222,19 @@ typedef struct SurfaceDeformModifierData { struct Object *target; /** Vertex bind data. */ SDefVert *verts; + void *_pad1; float falloff; - unsigned int mesh_verts_num, bind_verts_num, polys_num; + /* Number of of vertices on the deformed mesh upon the bind process. */ + unsigned int mesh_verts_num; + /* Number of vertices in the `verts` array of this modifier. */ + unsigned int bind_verts_num; + /* Number of vertices and polygons on the target mesh upon bind process. */ + unsigned int target_verts_num, target_polys_num; int flags; float mat[4][4]; float strength; char defgrp_name[64]; - void *_pad1; + int _pad2; } SurfaceDeformModifierData; /** Surface Deform modifier flags. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index e7e8ab3dd61..a0738883bf3 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -71,11 +71,22 @@ namespace blender::nodes { class NodeDeclaration; class SocketDeclaration; } // namespace blender::nodes +namespace blender::bke { +class bNodeTreeRuntime; +class bNodeRuntime; +class bNodeSocketRuntime; +} // namespace blender::bke using NodeDeclarationHandle = blender::nodes::NodeDeclaration; using SocketDeclarationHandle = blender::nodes::SocketDeclaration; +using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime; +using bNodeRuntimeHandle = blender::bke::bNodeRuntime; +using bNodeSocketRuntimeHandle = blender::bke::bNodeSocketRuntime; #else typedef struct NodeDeclarationHandle NodeDeclarationHandle; typedef struct SocketDeclarationHandle SocketDeclarationHandle; +typedef struct bNodeTreeRuntimeHandle bNodeTreeRuntimeHandle; +typedef struct bNodeRuntimeHandle bNodeRuntimeHandle; +typedef struct bNodeSocketRuntimeHandle bNodeSocketRuntimeHandle; #endif typedef struct bNodeSocket { @@ -169,15 +180,7 @@ typedef struct bNodeSocket { /** Custom data for inputs, only UI writes in this. */ bNodeStack ns DNA_DEPRECATED; - /** - * References a socket declaration that is owned by `node->declaration`. This is only runtime - * data. It has to be updated when the node declaration changes. - */ - const SocketDeclarationHandle *declaration; - - /** #eNodeTreeChangedFlag. */ - uint32_t changed_flag; - char _pad[4]; + bNodeSocketRuntimeHandle *runtime; } bNodeSocket; /** #bNodeSocket.type & #bNodeSocketType.type */ @@ -266,9 +269,7 @@ typedef struct bNode { /** Used as a boolean for execution. */ uint8_t need_exec; - char _pad2[5]; - /** #eNodeTreeChangedFlag. */ - uint32_t changed_flag; + char _pad2[1]; /** Custom user-defined color. */ float color[3]; @@ -331,25 +332,7 @@ typedef struct bNode { /** Used at runtime when iterating over node branches. */ char iter_flag; - /** - * Describes the desired interface of the node. This is run-time data only. - * The actual interface of the node may deviate from the declaration temporarily. - * It's possible to sync the actual state of the node to the desired state. Currently, this is - * only done when a node is created or loaded. - * - * In the future, we may want to keep more data only in the declaration, so that it does not have - * to be synced to other places that are stored in files. That especially applies to data that - * can't be edited by users directly (e.g. min/max values of sockets, tooltips, ...). - * - * The declaration of a node can be recreated at any time when it is used. Caching it here is - * just a bit more efficient when it is used a lot. To make sure that the cache is up-to-date, - * call #nodeDeclarationEnsure before using it. - * - * Currently, the declaration is the same for every node of the same type. Going forward, that is - * intended to change though. Especially when nodes become more dynamic with respect to how many - * sockets they have. - */ - NodeDeclarationHandle *declaration; + bNodeRuntimeHandle *runtime; } bNode; /* node->flag */ @@ -462,16 +445,6 @@ typedef struct bNodeLink { #define NTREE_CHUNKSIZE_512 512 #define NTREE_CHUNKSIZE_1024 1024 -/** Workaround to forward-declare C++ type in C header. */ -#ifdef __cplusplus -namespace blender::nodes { -struct FieldInferencingInterface; -} -using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface; -#else -typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle; -#endif - /* the basis for a Node tree, all links and nodes reside internal here */ /* only re-usable node trees are in the library though, * materials and textures allocate own tree struct */ @@ -494,31 +467,15 @@ typedef struct bNodeTree { float view_center[2]; ListBase nodes, links; - /** Information about how inputs and outputs of the node group interact with fields. */ - FieldInferencingInterfaceHandle *field_inferencing_interface; int type; /** - * Used to cache run-time information of the node tree. - * #eNodeTreeRuntimeFlag. - */ - uint8_t runtime_flag; - - char _pad1[3]; - - /** * Sockets in groups have unique identifiers, adding new sockets always * will increase this counter. */ int cur_index; int flag; - /** - * Keeps track of what changed in the node tree until the next update. - * Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`. - * #eNodeTreeChangedFlag. - */ - uint32_t changed_flag; /** Flag to prevent re-entrant update calls. */ short is_updating; /** Generic temporary flag for recursion check (DFS/BFS). */ @@ -552,11 +509,8 @@ typedef struct bNodeTree { * in case multiple different editors are used and make context ambiguous. */ bNodeInstanceKey active_viewer_key; - /** - * A hash of the topology of the node tree leading up to the outputs. This is used to determine - * of the node tree changed in a way that requires updating geometry nodes or shaders. - */ - uint32_t output_topology_hash; + + char _pad[4]; /** Execution data. * @@ -579,6 +533,8 @@ typedef struct bNodeTree { /** Image representing what the node group does. */ struct PreviewImage *preview; + + bNodeTreeRuntimeHandle *runtime; } bNodeTree; /** #NodeTree.type, index */ @@ -868,6 +824,12 @@ typedef struct NodeVertexCol { char name[64]; } NodeVertexCol; +typedef struct NodeCMPCombSepColor { + /* CMPNodeCombSepColorMode */ + uint8_t mode; + uint8_t ycc_mode; +} NodeCMPCombSepColor; + /** Defocus blur node. */ typedef struct NodeDefocus { char bktype, _pad0, preview, gamco; @@ -1498,6 +1460,11 @@ typedef struct NodeFunctionCompare { char _pad[1]; } NodeFunctionCompare; +typedef struct NodeCombSepColor { + /* NodeCombSepColorMode */ + int8_t mode; +} NodeCombSepColor; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1890,6 +1857,16 @@ typedef enum CMPNodeDenoisePrefilter { CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2 } CMPNodeDenoisePrefilter; +/* Color combine/separate modes */ + +typedef enum CMPNodeCombSepColorMode { + CMP_NODE_COMBSEP_COLOR_RGB = 0, + CMP_NODE_COMBSEP_COLOR_HSV = 1, + CMP_NODE_COMBSEP_COLOR_HSL = 2, + CMP_NODE_COMBSEP_COLOR_YCC = 3, + CMP_NODE_COMBSEP_COLOR_YUV = 4, +} CMPNodeCombSepColorMode; + #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 /* Point Density shader node */ @@ -2148,6 +2125,12 @@ typedef enum GeometryNodeScaleElementsMode { GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1, } GeometryNodeScaleElementsMode; +typedef enum NodeCombSepColorMode { + NODE_COMBSEP_COLOR_RGB = 0, + NODE_COMBSEP_COLOR_HSV = 1, + NODE_COMBSEP_COLOR_HSL = 2, +} NodeCombSepColorMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 9ea1e3a9e8d..f257833efe8 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -535,7 +535,8 @@ enum { /** Matches #OB_TYPE_SUPPORT_EDITMODE. */ #define OB_DATA_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR, ID_CV)) + (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR) || \ + (U.experimental.use_new_curves_tools && (_type) == ID_CV)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index d28550b5456..a54fd838bbe 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -161,7 +161,7 @@ typedef struct Sequence { * Frames that use the first frame before data begins, * frames that use the last frame after data ends. */ - int startstill, endstill; + int startstill DNA_DEPRECATED, endstill DNA_DEPRECATED; /** Machine: the strip channel */ int machine; int _pad3; diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h index df59dd84662..ba926f0f4fa 100644 --- a/source/blender/makesdna/DNA_sound_types.h +++ b/source/blender/makesdna/DNA_sound_types.h @@ -77,6 +77,12 @@ typedef struct bSound { void *spinlock; /* XXX unused currently (SOUND_TYPE_LIMITER) */ /* float start, end; */ + + /* Description of Audio channels, as of eSoundChannels*/ + int audio_channels; + + int samplerate; + } bSound; /* XXX unused currently */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index c1e96bcfaf3..85649a31ea4 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -173,7 +173,7 @@ typedef struct SpaceProperties { /* button defines (deprecated) */ #ifdef DNA_DEPRECATED_ALLOW -/* warning: the values of these defines are used in SpaceProperties.tabs[8] */ +/* WARNING: the values of these defines are used in SpaceProperties.tabs[8] */ /* SpaceProperties.mainb new */ # define CONTEXT_SCENE 0 # define CONTEXT_OBJECT 1 @@ -1223,7 +1223,8 @@ typedef struct SpaceImage { char dt_uvstretch; char around; - char _pad1[4]; + char gizmo_flag; + char _pad1[3]; int flag; @@ -1321,6 +1322,13 @@ typedef enum eSpaceImageOverlay_Flag { SI_OVERLAY_SHOW_GRID_BACKGROUND = (1 << 1), } eSpaceImageOverlay_Flag; +/** #SpaceImage.gizmo_flag */ +enum { + /** All gizmos. */ + SI_GIZMO_HIDE = (1 << 0), + SI_GIZMO_HIDE_NAVIGATE = (1 << 1), +}; + /** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */ #define SI_GRID_STEPS_LEN 8 @@ -1332,7 +1340,7 @@ typedef enum eSpaceImageOverlay_Flag { typedef struct SpaceText_Runtime { - /** Actual line height, scaled by dpi. */ + /** Actual line height, scaled by DPI. */ int lheight_px; /** Runtime computed, character width. */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 21abb632b94..275a89ec680 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -57,7 +57,7 @@ typedef struct uiFontStyle { /** Saved in file, 0 is default. */ short uifont_id; char _pad1[2]; - /** Actual size depends on 'global' dpi. */ + /** Actual size depends on 'global' DPI. */ float points; /** Style hint. */ short italic, bold; @@ -643,6 +643,8 @@ typedef struct UserDef_Experimental { /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ char use_new_curves_type; + /** Only available when #use_new_curves_type is enabled. */ + char use_new_curves_tools; char use_new_point_cloud_type; char use_full_frame_compositor; char use_sculpt_tools_tilt; @@ -651,7 +653,6 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; char use_draw_manager_acquire_lock; - char _pad0[1]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_volume_defaults.h b/source/blender/makesdna/DNA_volume_defaults.h index ee98f0ea4fd..1057fc75d34 100644 --- a/source/blender/makesdna/DNA_volume_defaults.h +++ b/source/blender/makesdna/DNA_volume_defaults.h @@ -23,6 +23,7 @@ #define _DNA_DEFAULT_VolumeRender \ { \ + .precision = VOLUME_PRECISION_HALF, \ .space = VOLUME_SPACE_OBJECT, \ .step_size = 0.0f, \ .clipping = 0.001f, \ diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h index a2e558aa790..a25bfe0ebec 100644 --- a/source/blender/makesdna/DNA_volume_types.h +++ b/source/blender/makesdna/DNA_volume_types.h @@ -126,6 +126,13 @@ typedef enum VolumeWireframeDetail { VOLUME_WIREFRAME_FINE = 1, } VolumeWireframeDetail; +/** #VolumeRender.precision */ +typedef enum VolumeRenderPrecision { + VOLUME_PRECISION_HALF = 0, + VOLUME_PRECISION_FULL = 1, + VOLUME_PRECISION_VARIABLE = 2, +} VolumeRenderPrecision; + /** #VolumeRender.space */ typedef enum VolumeRenderSpace { VOLUME_SPACE_OBJECT = 0, diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index 2afaf04a8d7..c26696b4572 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -13,6 +13,19 @@ blender_include_dirs( .. ) +set(dna_header_include_file "${CMAKE_CURRENT_BINARY_DIR}/dna_includes_all.h") +set(dna_header_string_file "${CMAKE_CURRENT_BINARY_DIR}/dna_includes_as_strings.h") + +set(DNA_INCLUDE_TEXT "/* Do not edit manually, changes will be overwritten. */\n") +set(DNA_FILE_LIST "/* Do not edit manually, changes will be overwritten. */\n") +foreach(header ${SRC_DNA_INC}) + get_filename_component(dna_header_file ${header} NAME) + string(APPEND DNA_INCLUDE_TEXT "#include \"${header}\"\n") + string(APPEND DNA_FILE_LIST "\t\"${dna_header_file}\",\n") +endforeach() + +file(GENERATE OUTPUT ${dna_header_include_file} CONTENT "${DNA_INCLUDE_TEXT}") +file(GENERATE OUTPUT ${dna_header_string_file} CONTENT "${DNA_FILE_LIST}") # ----------------------------------------------------------------------------- # Build makesdna executable @@ -29,6 +42,8 @@ set(SRC ../../../../intern/guardedalloc/intern/mallocn.c ../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c + ${dna_header_include_file} + ${dna_header_string_file} ) # SRC_DNA_INC is defined in the parent dir @@ -115,40 +130,7 @@ set(SRC ../../blenlib/intern/hash_mm2a.c ../../blenlib/intern/listbase.c - ../DNA_armature_defaults.h - ../DNA_asset_defaults.h - ../DNA_brush_defaults.h - ../DNA_cachefile_defaults.h - ../DNA_camera_defaults.h - ../DNA_collection_defaults.h - ../DNA_curve_defaults.h - ../DNA_curves_defaults.h - ../DNA_defaults.h - ../DNA_fluid_defaults.h - ../DNA_gpencil_modifier_defaults.h - ../DNA_image_defaults.h - ../DNA_lattice_defaults.h - ../DNA_light_defaults.h - ../DNA_lightprobe_defaults.h - ../DNA_linestyle_defaults.h - ../DNA_material_defaults.h - ../DNA_mesh_defaults.h - ../DNA_meta_defaults.h - ../DNA_modifier_defaults.h - ../DNA_modifier_types.h - ../DNA_movieclip_defaults.h - ../DNA_object_defaults.h - ../DNA_particle_defaults.h - ../DNA_pointcloud_defaults.h - ../DNA_scene_defaults.h - ../DNA_simulation_defaults.h - ../DNA_space_defaults.h - ../DNA_speaker_defaults.h - ../DNA_texture_defaults.h - ../DNA_vec_defaults.h - ../DNA_view3d_defaults.h - ../DNA_volume_defaults.h - ../DNA_world_defaults.h + ${SRC_DNA_DEFAULTS_INC} ) set(LIB diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 86649357433..f25ff5fbbb8 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -61,6 +61,8 @@ DNA_STRUCT_RENAME_ELEM(Curve, ext1, extrude) DNA_STRUCT_RENAME_ELEM(Curve, ext2, bevel_radius) DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Curve, width, offset) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, curve_size, curve_num) +DNA_STRUCT_RENAME_ELEM(CurvesGeometry, point_size, point_num) DNA_STRUCT_RENAME_ELEM(CustomDataExternal, filename, filepath) DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) @@ -106,7 +108,7 @@ DNA_STRUCT_RENAME_ELEM(SDefBind, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(SDefVert, numbinds, binds_num) DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, num_mesh_verts, mesh_verts_num) -DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, polys_num) +DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, target_polys_num) DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, bind_verts_num) DNA_STRUCT_RENAME_ELEM(SurfaceModifierData, numverts, verts_num) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 12ec7262906..806513009be 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -43,92 +43,11 @@ #define SDNA_MAX_FILENAME_LENGTH 255 -/* Included the path relative from /source/blender/ here, - * so we can move headers around with more freedom. */ +/* The include file below is automatically generated from the `SRC_DNA_INC` + * variable in 'source/blender/CMakeLists.txt'. */ static const char *includefiles[] = { - /* if you add files here, please add them at the end - * of makesdna.c (this file) as well */ - "DNA_listBase.h", - "DNA_vec_types.h", - "DNA_ID.h", - "DNA_ipo_types.h", - "DNA_key_types.h", - "DNA_text_types.h", - "DNA_packedFile_types.h", - "DNA_gpu_types.h", - "DNA_camera_types.h", - "DNA_image_types.h", - "DNA_texture_types.h", - "DNA_light_types.h", - "DNA_material_types.h", - "DNA_vfont_types.h", - "DNA_meta_types.h", - "DNA_curve_types.h", - "DNA_mesh_types.h", - "DNA_meshdata_types.h", - "DNA_modifier_types.h", - "DNA_lineart_types.h", - "DNA_lattice_types.h", - "DNA_object_types.h", - "DNA_object_force_types.h", - "DNA_object_fluidsim_types.h", - "DNA_world_types.h", - "DNA_scene_types.h", - "DNA_view3d_types.h", - "DNA_view2d_types.h", - "DNA_space_types.h", - "DNA_userdef_types.h", - "DNA_screen_types.h", - "DNA_sdna_types.h", - "DNA_fileglobal_types.h", - "DNA_sequence_types.h", - "DNA_session_uuid_types.h", - "DNA_effect_types.h", - "DNA_outliner_types.h", - "DNA_sound_types.h", - "DNA_collection_types.h", - "DNA_armature_types.h", - "DNA_action_types.h", - "DNA_constraint_types.h", - "DNA_nla_types.h", - "DNA_node_types.h", - "DNA_color_types.h", - "DNA_brush_types.h", - "DNA_customdata_types.h", - "DNA_particle_types.h", - "DNA_cloth_types.h", - "DNA_gpencil_types.h", - "DNA_gpencil_modifier_types.h", - "DNA_shader_fx_types.h", - "DNA_windowmanager_types.h", - "DNA_anim_types.h", - "DNA_boid_types.h", - "DNA_fluid_types.h", - "DNA_speaker_types.h", - "DNA_movieclip_types.h", - "DNA_tracking_types.h", - "DNA_dynamicpaint_types.h", - "DNA_mask_types.h", - "DNA_rigidbody_types.h", - "DNA_freestyle_types.h", - "DNA_linestyle_types.h", - "DNA_cachefile_types.h", - "DNA_layer_types.h", - "DNA_workspace_types.h", - "DNA_lightprobe_types.h", - "DNA_curveprofile_types.h", - "DNA_xr_types.h", - "DNA_curves_types.h", - "DNA_pointcloud_types.h", - "DNA_volume_types.h", - "DNA_simulation_types.h", - "DNA_pointcache_types.h", - "DNA_uuid_types.h", - "DNA_asset_types.h", - - /* see comment above before editing! */ - - /* empty string to indicate end of includefiles */ +#include "dna_includes_as_strings.h" + /* Empty string to indicate end of include files. */ "", }; @@ -235,7 +154,7 @@ static int preprocess_include(char *maindata, const int maindata_len); /** * Scan this file for serializable types. */ -static int convert_include(const char *filename); +static int convert_include(const char *filepath); /** * Determine how many bytes are needed for each struct. @@ -670,12 +589,12 @@ static int preprocess_include(char *maindata, const int maindata_len) return newlen; } -static void *read_file_data(const char *filename, int *r_len) +static void *read_file_data(const char *filepath, int *r_len) { #ifdef WIN32 - FILE *fp = fopen(filename, "rb"); + FILE *fp = fopen(filepath, "rb"); #else - FILE *fp = fopen(filename, "r"); + FILE *fp = fopen(filepath, "r"); #endif void *data; @@ -711,17 +630,17 @@ static void *read_file_data(const char *filename, int *r_len) return data; } -static int convert_include(const char *filename) +static int convert_include(const char *filepath) { /* read include file, skip structs with a '#' before it. * store all data in temporal arrays. */ int maindata_len; - char *maindata = read_file_data(filename, &maindata_len); + char *maindata = read_file_data(filepath, &maindata_len); char *md = maindata; if (maindata_len == -1) { - fprintf(stderr, "Can't read file %s\n", filename); + fprintf(stderr, "Can't read file %s\n", filepath); return 1; } @@ -759,7 +678,7 @@ static int convert_include(const char *filename) const int strct = add_type(md1, 0); if (strct == -1) { - fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filename, md1); + fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1); return 1; } @@ -799,7 +718,7 @@ static int convert_include(const char *filename) fprintf(stderr, "File '%s' contains non white space character " "\"%c\" after identifier \"%s\"\n", - filename, + filepath, *md1, md1_prev); return 1; @@ -812,7 +731,7 @@ static int convert_include(const char *filename) const int type = add_type(md1, 0); if (type == -1) { fprintf( - stderr, "File '%s' contains struct we can't parse \"%s\"\n", filename, md1); + stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1); return 1; } @@ -838,7 +757,7 @@ static int convert_include(const char *filename) if (name == -1) { fprintf(stderr, "File '%s' contains struct with name that can't be added \"%s\"\n", - filename, + filepath, md1); return 1; } @@ -861,7 +780,7 @@ static int convert_include(const char *filename) if (name == -1) { fprintf(stderr, "File '%s' contains struct with name that can't be added \"%s\"\n", - filename, + filepath, md1); return 1; } @@ -1617,82 +1536,9 @@ int main(int argc, char **argv) # pragma GCC poison long #endif -#include "DNA_ID.h" -#include "DNA_action_types.h" -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_asset_types.h" -#include "DNA_boid_types.h" -#include "DNA_brush_types.h" -#include "DNA_cachefile_types.h" -#include "DNA_camera_types.h" -#include "DNA_cloth_types.h" -#include "DNA_collection_types.h" -#include "DNA_color_types.h" -#include "DNA_constraint_types.h" -#include "DNA_curve_types.h" -#include "DNA_curveprofile_types.h" -#include "DNA_curves_types.h" -#include "DNA_customdata_types.h" -#include "DNA_dynamicpaint_types.h" -#include "DNA_effect_types.h" -#include "DNA_fileglobal_types.h" -#include "DNA_fluid_types.h" -#include "DNA_freestyle_types.h" -#include "DNA_gpencil_modifier_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_image_types.h" -#include "DNA_ipo_types.h" -#include "DNA_key_types.h" -#include "DNA_lattice_types.h" -#include "DNA_layer_types.h" -#include "DNA_light_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_lineart_types.h" -#include "DNA_linestyle_types.h" -#include "DNA_listBase.h" -#include "DNA_mask_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_meta_types.h" -#include "DNA_modifier_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_nla_types.h" -#include "DNA_node_types.h" -#include "DNA_object_fluidsim_types.h" -#include "DNA_object_force_types.h" -#include "DNA_object_types.h" -#include "DNA_outliner_types.h" -#include "DNA_packedFile_types.h" -#include "DNA_particle_types.h" -#include "DNA_pointcache_types.h" -#include "DNA_pointcloud_types.h" -#include "DNA_rigidbody_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_sdna_types.h" -#include "DNA_sequence_types.h" -#include "DNA_session_uuid_types.h" -#include "DNA_shader_fx_types.h" -#include "DNA_simulation_types.h" -#include "DNA_sound_types.h" -#include "DNA_space_types.h" -#include "DNA_speaker_types.h" -#include "DNA_text_types.h" -#include "DNA_texture_types.h" -#include "DNA_tracking_types.h" -#include "DNA_userdef_types.h" -#include "DNA_uuid_types.h" -#include "DNA_vec_types.h" -#include "DNA_vfont_types.h" -#include "DNA_view2d_types.h" -#include "DNA_view3d_types.h" -#include "DNA_volume_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" -#include "DNA_world_types.h" -#include "DNA_xr_types.h" +/* The include file below is automatically generated from the `SRC_DNA_INC` + * variable in 'source/blender/CMakeLists.txt'. */ +#include "dna_includes_all.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e1e655fad4b..e855395482e 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -177,7 +177,7 @@ int RNA_property_multi_array_length(PointerRNA *ptr, PropertyRNA *prop, int dime /** * Used by BPY to make an array from the python object. */ -int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[]); +int RNA_property_array_dimension(const PointerRNA *ptr, PropertyRNA *prop, int length[]); char RNA_property_array_item_char(PropertyRNA *prop, int index); int RNA_property_array_item_index(PropertyRNA *prop, char name); @@ -284,7 +284,7 @@ bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int i */ bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop); -bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop); +bool RNA_property_animateable(const PointerRNA *ptr, PropertyRNA *prop); bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop); /** * \note Does not take into account editable status, this has to be checked separately @@ -358,6 +358,21 @@ char *RNA_property_string_get_alloc( PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len); void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value); void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len); + +eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop); +/** + * Search candidates for string `prop` by calling `visit_fn` with each string. + * Typically these strings are collected in `visit_user_data` in a format defined by the caller. + * + * See #PropStringSearchFunc for details. + */ +void RNA_property_string_search(const struct bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /** * \return the length without `\0` terminator. */ @@ -402,7 +417,9 @@ int RNA_property_collection_length(PointerRNA *ptr, PropertyRNA *prop); * without having to iterate over items in the collection (needed for some kinds of collections). */ bool RNA_property_collection_is_empty(PointerRNA *ptr, PropertyRNA *prop); -int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr); +int RNA_property_collection_lookup_index(PointerRNA *ptr, + PropertyRNA *prop, + const PointerRNA *t_ptr); int RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, @@ -468,7 +485,7 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop); * UI code or Actions, though efficiency is a concern. */ char *RNA_path_append( - const char *path, PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); + const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); #if 0 /* UNUSED. */ char *RNA_path_back(const char *path); #endif @@ -486,7 +503,10 @@ char *RNA_path_back(const char *path); * \note Assumes all pointers provided are valid * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" */ -bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop); /** * Resolve the given RNA Path to find the pointer and/or property + array index @@ -495,16 +515,22 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop * \note Assumes all pointers provided are valid. * \return True if path can be resolved to a valid "pointer + property" OR "pointer only" */ -bool RNA_path_resolve_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /** * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. * * \note While it's correct to ignore the value of #PointerRNA.data * most callers need to know if the resulting pointer was found and not null. */ -bool RNA_path_resolve_full_maybe_null( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /* RNA_path_resolve_property() variants ensure that pointer + property both exist. */ @@ -516,7 +542,7 @@ bool RNA_path_resolve_full_maybe_null( * \note Assumes all pointers provided are valid * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property(PointerRNA *ptr, +bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); @@ -529,8 +555,11 @@ bool RNA_path_resolve_property(PointerRNA *ptr, * \note Assumes all pointers provided are valid * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_property_full(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index); /* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist, * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */ @@ -547,7 +576,7 @@ bool RNA_path_resolve_property_full( * You must check for its validity before use! * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -566,7 +595,7 @@ bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, * You must check for its validity before use! * \return True only if both a valid pointer and property are found after resolving the path */ -bool RNA_path_resolve_property_and_item_pointer_full(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -614,21 +643,23 @@ struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const ch char *RNA_path_from_ID_to_struct(const PointerRNA *ptr); -char *RNA_path_from_real_ID_to_struct(struct Main *bmain, PointerRNA *ptr, struct ID **r_real); +char *RNA_path_from_real_ID_to_struct(struct Main *bmain, + const PointerRNA *ptr, + struct ID **r_real); -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop); +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop); /** * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc. * \param index: The *flattened* index to use when \a `index_dim > 0`, * this is expanded when used with multi-dimensional arrays. */ -char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index); char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, - PointerRNA *ptr, + const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, @@ -638,8 +669,8 @@ char *RNA_path_from_real_ID_to_property_index(struct Main *bmain, * \return the path to given ptr/prop from the closest ancestor of given type, * if any (else return NULL). */ -char *RNA_path_resolve_from_type_to_property(struct PointerRNA *ptr, - struct PropertyRNA *prop, +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, + PropertyRNA *prop, const struct StructRNA *type); /** @@ -651,27 +682,27 @@ char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id); * Get the ID.struct as a python representation, eg: * bpy.data.foo["bar"].some_struct */ -char *RNA_path_full_struct_py(struct Main *bmain, struct PointerRNA *ptr); +char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr); /** * Get the ID.struct.property as a python representation, eg: * bpy.data.foo["bar"].some_struct.some_prop[10] */ char *RNA_path_full_property_py_ex( - struct Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); + struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback); char *RNA_path_full_property_py(struct Main *bmain, - struct PointerRNA *ptr, - struct PropertyRNA *prop, + const PointerRNA *ptr, + PropertyRNA *prop, int index); /** * Get the struct.property as a python representation, eg: * some_struct.some_prop[10] */ -char *RNA_path_struct_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index); /** * Get the struct.property as a python representation, eg: * some_prop[10] */ -char *RNA_path_property_py(const struct PointerRNA *ptr, struct PropertyRNA *prop, int index); +char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index); /* Quick name based property access * diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index 13a5ec66a16..0389d1b3b16 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -446,6 +446,9 @@ void RNA_def_property_string_funcs(PropertyRNA *prop, const char *get, const char *length, const char *set); +void RNA_def_property_string_search_func(PropertyRNA *prop, + const char *search, + eStringPropertySearchFlag search_flag); void RNA_def_property_pointer_funcs( PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll); void RNA_def_property_collection_funcs(PropertyRNA *prop, @@ -490,6 +493,9 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, StringPropertyGetFunc getfunc, StringPropertyLengthFunc lengthfunc, StringPropertySetFunc setfunc); +void RNA_def_property_string_search_func_runtime(PropertyRNA *prop, + StringPropertySearchFunc search_fn, + eStringPropertySearchFlag search_flag); void RNA_def_property_translation_context(PropertyRNA *prop, const char *context); diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 74f8e2487a4..37af598729b 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -108,7 +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_curves_sculpt_tool_items) DEF_ENUM(rna_enum_brush_image_tool_items) DEF_ENUM(rna_enum_axis_xy_items) @@ -150,7 +150,15 @@ DEF_ENUM(rna_enum_wm_report_items) DEF_ENUM(rna_enum_property_type_items) DEF_ENUM(rna_enum_property_subtype_items) +DEF_ENUM(rna_enum_property_subtype_string_items) +DEF_ENUM(rna_enum_property_subtype_number_items) +DEF_ENUM(rna_enum_property_subtype_number_array_items) DEF_ENUM(rna_enum_property_unit_items) +DEF_ENUM(rna_enum_property_flag_items) +DEF_ENUM(rna_enum_property_flag_enum_items) +DEF_ENUM(rna_enum_property_override_flag_items) +DEF_ENUM(rna_enum_property_override_flag_collection_items) +DEF_ENUM(rna_enum_property_string_search_flag_items) DEF_ENUM(rna_enum_shading_type_items) @@ -198,8 +206,11 @@ DEF_ENUM(rna_enum_context_mode_items) DEF_ENUM(rna_enum_preference_section_items) DEF_ENUM(rna_enum_attribute_type_items) +DEF_ENUM(rna_enum_color_attribute_type_items) DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_attribute_curves_domain_items) +DEF_ENUM(rna_enum_color_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) DEF_ENUM(rna_enum_geometry_component_type_items) diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 3ebcae5f947..5346228050a 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -467,6 +467,27 @@ typedef struct EnumPropertyItem { const char *description; } EnumPropertyItem; +/** + * Heading for RNA enum items (shown in the UI). + * + * The description is currently only shown in the Python documentation. + * By convention the value should be a non-empty string or NULL when there is no description + * (never an empty string). + */ +#define RNA_ENUM_ITEM_HEADING(name, description) \ + { \ + 0, "", 0, name, description \ + } + +/** Separator for RNA enum items (shown in the UI). */ +#define RNA_ENUM_ITEM_SEPR \ + { \ + 0, "", 0, NULL, NULL \ + } + +/** Separator for RNA enum that begins a new column in menus (shown in the UI). */ +#define RNA_ENUM_ITEM_SEPR_COLUMN RNA_ENUM_ITEM_HEADING("", NULL) + /* extended versions with PropertyRNA argument */ typedef bool (*BooleanPropertyGetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*BooleanPropertySetFunc)(struct PointerRNA *ptr, @@ -515,6 +536,55 @@ typedef int (*StringPropertyLengthFunc)(struct PointerRNA *ptr, struct PropertyR typedef void (*StringPropertySetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop, const char *value); + +typedef struct StringPropertySearchVisitParams { + /** Text being searched for (never NULL). */ + const char *text; + /** Additional information to display (optional, may be NULL). */ + const char *info; +} StringPropertySearchVisitParams; + +typedef enum eStringPropertySearchFlag { + /** + * Used so the result of #RNA_property_string_search_flag can be used to check + * if search is supported. + */ + PROP_STRING_SEARCH_SUPPORTED = (1 << 0), + /** Items resulting from the search must be sorted. */ + PROP_STRING_SEARCH_SORT = (1 << 1), + /** + * Allow members besides the ones listed to be entered. + * + * \warning disabling this options causes the search callback to run on redraw and should + * only be enabled this doesn't cause performance issues. + */ + PROP_STRING_SEARCH_SUGGESTION = (1 << 2), +} eStringPropertySearchFlag; + +/** + * Visit string search candidates, `text` may be freed once this callback has finished, + * so references to it should not be held. + */ +typedef void (*StringPropertySearchVisitFunc)(void *visit_user_data, + const StringPropertySearchVisitParams *params); +/** + * \param C: context, may be NULL (in this case all available items should be shown). + * \param ptr: RNA pointer. + * \param prop: RNA property. This must have it's #StringPropertyRNA.search callback set, + * to check this use `RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED`. + * \param edit_text: Optionally use the string being edited by the user as a basis + * for the search results (auto-complete Python attributes for e.g.). + * \param visit_fn: This function is called with every search candidate and is typically + * responsible for storing the search results. + * \param visit_user_data: Caller defined data, passed to `visit_fn`. + */ +typedef void (*StringPropertySearchFunc)(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + typedef int (*EnumPropertyGetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*EnumPropertySetFunc)(struct PointerRNA *ptr, struct PropertyRNA *prop, int value); /* same as PropEnumItemFunc */ diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 9980545c19d..af8767a1220 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -237,7 +237,7 @@ if(WITH_IMAGE_HDR) endif() if(WITH_IMAGE_WEBP) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() if(WITH_AUDASPACE) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index c3ca57b38bf..400944d60d4 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1039,6 +1039,38 @@ static void rna_clamp_value(FILE *f, PropertyRNA *prop, int array) } } +static char *rna_def_property_search_func(FILE *f, + StructRNA *srna, + PropertyRNA *prop, + PropertyDefRNA *UNUSED(dp), + const char *manualfunc) +{ + char *func; + + if (prop->flag & PROP_IDPROPERTY && manualfunc == NULL) { + return NULL; + } + if (!manualfunc) { + return NULL; + } + + func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "search"); + + fprintf(f, + "void %s(" + "const bContext *C, " + "PointerRNA *ptr, " + "PropertyRNA *prop, " + "const char *edit_text, " + "StringPropertySearchVisitFunc visit_fn, " + "void *visit_user_data)\n", + func); + fprintf(f, "{\n"); + fprintf(f, "\n %s(C, ptr, prop, edit_text, visit_fn, visit_user_data);\n", manualfunc); + fprintf(f, "}\n\n"); + return func; +} + static char *rna_def_property_set_func( FILE *f, StructRNA *srna, PropertyRNA *prop, PropertyDefRNA *dp, const char *manualfunc) { @@ -1895,6 +1927,8 @@ static void rna_def_property_funcs(FILE *f, StructRNA *srna, PropertyDefRNA *dp) sprop->length = (void *)rna_def_property_length_func( f, srna, prop, dp, (const char *)sprop->length); sprop->set = (void *)rna_def_property_set_func(f, srna, prop, dp, (const char *)sprop->set); + sprop->search = (void *)rna_def_property_search_func( + f, srna, prop, dp, (const char *)sprop->search); break; } case PROP_POINTER: { @@ -4081,13 +4115,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr case PROP_STRING: { StringPropertyRNA *sprop = (StringPropertyRNA *)prop; fprintf(f, - "\t%s, %s, %s, %s, %s, %s, %d, ", + "\t%s, %s, %s, %s, %s, %s, %s, %d, %d, ", rna_function_string(sprop->get), rna_function_string(sprop->length), rna_function_string(sprop->set), rna_function_string(sprop->get_ex), rna_function_string(sprop->length_ex), rna_function_string(sprop->set_ex), + rna_function_string(sprop->search), + (int)sprop->search_flag, sprop->maxlength); rna_print_c_string(f, sprop->defaultvalue); fprintf(f, "\n"); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b0488bbfa7a..b5cf8abaac6 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -722,7 +722,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, id_instance_hint, &id_root_override); + bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override, false); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); @@ -1131,7 +1131,7 @@ static void rna_ImagePreview_size_set(PointerRNA *ptr, const int *values, enum e prv_img->flag[size] |= (PRV_CHANGED | PRV_USER_EDITED); } -static int rna_ImagePreview_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION], enum eIconSizes size) { @@ -1176,7 +1176,7 @@ static void rna_ImagePreview_pixels_set(PointerRNA *ptr, const int *values, enum prv_img->flag[size] |= PRV_USER_EDITED; } -static int rna_ImagePreview_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION], enum eIconSizes size) { @@ -1256,7 +1256,7 @@ static void rna_ImagePreview_image_size_set(PointerRNA *ptr, const int *values) rna_ImagePreview_size_set(ptr, values, ICON_SIZE_PREVIEW); } -static int rna_ImagePreview_image_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_image_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_PREVIEW); @@ -1272,7 +1272,7 @@ static void rna_ImagePreview_image_pixels_set(PointerRNA *ptr, const int *values rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_PREVIEW); } -static int rna_ImagePreview_image_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_image_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_float_get_length(ptr, length, ICON_SIZE_PREVIEW); @@ -1303,7 +1303,7 @@ static void rna_ImagePreview_icon_size_set(PointerRNA *ptr, const int *values) rna_ImagePreview_size_set(ptr, values, ICON_SIZE_ICON); } -static int rna_ImagePreview_icon_pixels_get_length(PointerRNA *ptr, +static int rna_ImagePreview_icon_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_get_length(ptr, length, ICON_SIZE_ICON); @@ -1319,7 +1319,7 @@ static void rna_ImagePreview_icon_pixels_set(PointerRNA *ptr, const int *values) rna_ImagePreview_pixels_set(ptr, values, ICON_SIZE_ICON); } -static int rna_ImagePreview_icon_pixels_float_get_length(PointerRNA *ptr, +static int rna_ImagePreview_icon_pixels_float_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { return rna_ImagePreview_pixels_float_get_length(ptr, length, ICON_SIZE_ICON); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 8f7660d4015..75579107465 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -302,7 +302,8 @@ static int rna_ensure_property_array_length(PointerRNA *ptr, PropertyRNA *prop) { if (prop->magic == RNA_MAGIC) { int arraylen[RNA_MAX_ARRAY_DIMENSION]; - return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : prop->totarraylength; + return (prop->getlength && ptr->data) ? prop->getlength(ptr, arraylen) : + (int)prop->totarraylength; } IDProperty *idprop = (IDProperty *)prop; @@ -322,7 +323,7 @@ static bool rna_ensure_property_array_check(PropertyRNA *prop) return (idprop->type == IDP_ARRAY); } -static void rna_ensure_property_multi_array_length(PointerRNA *ptr, +static void rna_ensure_property_multi_array_length(const PointerRNA *ptr, PropertyRNA *prop, int length[]) { @@ -1080,7 +1081,7 @@ bool RNA_property_array_check(PropertyRNA *prop) return rna_ensure_property_array_check(prop); } -int RNA_property_array_dimension(PointerRNA *ptr, PropertyRNA *prop, int length[]) +int RNA_property_array_dimension(const PointerRNA *ptr, PropertyRNA *prop, int length[]) { PropertyRNA *rprop = rna_ensure_property(prop); @@ -1988,7 +1989,7 @@ bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int i return rna_property_editable_do(ptr, prop, index, NULL); } -bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop) +bool RNA_property_animateable(const PointerRNA *ptr, PropertyRNA *prop) { /* check that base ID-block can support animation data */ if (!id_can_have_animdata(ptr->owner_id)) { @@ -3368,6 +3369,34 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro return strlen(sprop->defaultvalue); } +eStringPropertySearchFlag RNA_property_string_search_flag(PropertyRNA *prop) +{ + StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); + if (prop->magic != RNA_MAGIC) { + return false; + } + BLI_assert(RNA_property_type(prop) == PROP_STRING); + if (sprop->search) { + BLI_assert(sprop->search_flag & PROP_STRING_SEARCH_SUPPORTED); + } + else { + BLI_assert(sprop->search_flag == 0); + } + return sprop->search_flag; +} + +void RNA_property_string_search(const bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + BLI_assert(RNA_property_string_search_flag(prop) & PROP_STRING_SEARCH_SUPPORTED); + StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); + sprop->search(C, ptr, prop, edit_text, visit_fn, visit_user_data); +} + int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop) { EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop; @@ -4025,7 +4054,9 @@ void RNA_property_collection_clear(PointerRNA *ptr, PropertyRNA *prop) } } -int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *t_ptr) +int RNA_property_collection_lookup_index(PointerRNA *ptr, + PropertyRNA *prop, + const PointerRNA *t_ptr) { CollectionPropertyIterator iter; int index = 0; @@ -5105,7 +5136,7 @@ static bool rna_path_parse_array_index(const char **path, * * \return \a true on success, \a false if the path is somehow invalid. */ -static bool rna_path_parse(PointerRNA *ptr, +static bool rna_path_parse(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5262,7 +5293,10 @@ static bool rna_path_parse(PointerRNA *ptr, return true; } -bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop) +bool RNA_path_resolve(const PointerRNA *ptr, + const char *path, + PointerRNA *r_ptr, + PropertyRNA **r_prop) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) { return false; @@ -5272,7 +5306,7 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop } bool RNA_path_resolve_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) { return false; @@ -5282,12 +5316,12 @@ bool RNA_path_resolve_full( } bool RNA_path_resolve_full_maybe_null( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); } -bool RNA_path_resolve_property(PointerRNA *ptr, +bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop) @@ -5300,7 +5334,7 @@ bool RNA_path_resolve_property(PointerRNA *ptr, } bool RNA_path_resolve_property_full( - PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) + const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) { if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) { return false; @@ -5309,7 +5343,7 @@ bool RNA_path_resolve_property_full( return r_ptr->data != NULL && *r_prop != NULL; } -bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5322,7 +5356,7 @@ bool RNA_path_resolve_property_and_item_pointer(PointerRNA *ptr, return r_ptr->data != NULL && *r_prop != NULL; } -bool RNA_path_resolve_property_and_item_pointer_full(PointerRNA *ptr, +bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, @@ -5340,8 +5374,11 @@ bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_el return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false); } -char *RNA_path_append( - const char *path, PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey) +char *RNA_path_append(const char *path, + const PointerRNA *UNUSED(ptr), + PropertyRNA *prop, + int intkey, + const char *strkey) { DynStr *dynstr; char *result; @@ -5724,7 +5761,7 @@ char *RNA_path_from_ID_to_struct(const PointerRNA *ptr) return ptrpath; } -char *RNA_path_from_real_ID_to_struct(Main *bmain, PointerRNA *ptr, struct ID **r_real) +char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real) { char *path = RNA_path_from_ID_to_struct(ptr); @@ -5753,7 +5790,7 @@ static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY BLI_assert(index == 0); } -static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr, +static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, @@ -5772,7 +5809,7 @@ static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr, } } -char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, +char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index) @@ -5828,13 +5865,17 @@ char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, return path; } -char *RNA_path_from_ID_to_property(PointerRNA *ptr, PropertyRNA *prop) +char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop) { return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1); } -char *RNA_path_from_real_ID_to_property_index( - Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index_dim, int index, ID **r_real_id) +char *RNA_path_from_real_ID_to_property_index(Main *bmain, + const PointerRNA *ptr, + PropertyRNA *prop, + int index_dim, + int index, + ID **r_real_id) { char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index); @@ -5843,7 +5884,7 @@ char *RNA_path_from_real_ID_to_property_index( return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL; } -char *RNA_path_resolve_from_type_to_property(PointerRNA *ptr, +char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr, PropertyRNA *prop, const StructRNA *type) { @@ -5916,7 +5957,7 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id) path); } -char *RNA_path_full_struct_py(Main *bmain, struct PointerRNA *ptr) +char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr) { char *id_path; char *data_path; @@ -5945,7 +5986,7 @@ char *RNA_path_full_struct_py(Main *bmain, struct PointerRNA *ptr) } char *RNA_path_full_property_py_ex( - Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) + Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback) { char *id_path; const char *data_delim; @@ -5992,7 +6033,7 @@ char *RNA_path_full_property_py_ex( return ret; } -char *RNA_path_full_property_py(Main *bmain, PointerRNA *ptr, PropertyRNA *prop, int index) +char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index) { return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false); } @@ -6723,23 +6764,27 @@ static void *rna_array_as_string_alloc( int type, int len, PointerRNA *ptr, PropertyRNA *prop, void **r_buf_end) { void *buf_ret = NULL; - if (type == PROP_BOOLEAN) { - bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_boolean_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else if (type == PROP_INT) { - int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_int_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else if (type == PROP_FLOAT) { - float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); - RNA_property_float_get_array(ptr, prop, buf); - *r_buf_end = buf + len; - } - else { - BLI_assert(0); + switch (type) { + case PROP_BOOLEAN: { + bool *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_boolean_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + case PROP_INT: { + int *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_int_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + case PROP_FLOAT: { + float *buf = buf_ret = MEM_mallocN(sizeof(*buf) * len, __func__); + RNA_property_float_get_array(ptr, prop, buf); + *r_buf_end = buf + len; + break; + } + default: + BLI_assert_unreachable(); } return buf_ret; } @@ -6749,29 +6794,33 @@ static void rna_array_as_string_elem(int type, void **buf_p, int len, DynStr *dy /* This will print a comma separated string of the array elements from * buf start to len. We will add a comma if len == 1 to preserve tuples. */ const int end = len - 1; - if (type == PROP_BOOLEAN) { - bool *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf)); + switch (type) { + case PROP_BOOLEAN: { + bool *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%s, " : "%s", bool_as_py_string(*buf)); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else if (type == PROP_INT) { - int *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf); + case PROP_INT: { + int *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%d, " : "%d", *buf); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else if (type == PROP_FLOAT) { - float *buf = *buf_p; - for (int i = 0; i < len; i++, buf++) { - BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf); + case PROP_FLOAT: { + float *buf = *buf_p; + for (int i = 0; i < len; i++, buf++) { + BLI_dynstr_appendf(dynstr, (i < end || !end) ? "%g, " : "%g", *buf); + } + *buf_p = buf; + break; } - *buf_p = buf; - } - else { - BLI_assert(0); + default: + BLI_assert_unreachable(); } } diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 5974788884e..17c00923efa 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -12,6 +12,7 @@ #include "DNA_ID.h" #include "DNA_anim_types.h" +#include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_key_types.h" @@ -87,7 +88,7 @@ static ID *rna_property_override_property_real_id_owner(Main *bmain, owner_id = RNA_find_real_ID_and_path(bmain, id, &rna_path_prefix); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } } @@ -145,6 +146,12 @@ bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop) return true; } } + else if (RNA_struct_is_a(ptr->type, &RNA_CameraBackgroundImage)) { + CameraBGImage *bgpic = ptr->data; + if (bgpic->flag & CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL) { + return true; + } + } /* If this is a RNA-defined property (real or 'virtual' IDProp), * we want to use RNA prop flag. */ return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) && @@ -354,7 +361,7 @@ static int rna_property_override_diff(Main *bmain, if (is_array_a != is_array_b) { /* Should probably never happen actually... */ - BLI_assert(0); + BLI_assert_unreachable(); return is_array_a ? 1 : -1; } @@ -400,7 +407,7 @@ static int rna_property_override_diff(Main *bmain, rna_path ? rna_path : prop_a->identifier, !prop_a->is_idprop, !prop_b->is_idprop); - BLI_assert(0); + BLI_assert_unreachable(); return 1; } @@ -491,7 +498,7 @@ static bool rna_property_override_operation_store(Main *bmain, op->rna_path, prop_local->magic == RNA_MAGIC, prop_reference->magic == RNA_MAGIC); - BLI_assert(0); + BLI_assert_unreachable(); return changed; } @@ -580,7 +587,7 @@ static bool rna_property_override_operation_apply(Main *bmain, prop_dst->identifier, prop_dst->magic == RNA_MAGIC, prop_src->magic == RNA_MAGIC); - BLI_assert(0); + BLI_assert_unreachable(); return false; } diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index 76d2087d904..a1266443631 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -340,7 +340,7 @@ bool rna_Action_actedit_assign_poll(PointerRNA *ptr, PointerRNA value) return 0; } -static char *rna_DopeSheet_path(PointerRNA *UNUSED(ptr)) +static char *rna_DopeSheet_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("dopesheet"); } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index df92601dd0c..a4094630266 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -216,10 +216,10 @@ static void rna_Bone_select_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN, id); } -static char *rna_Bone_path(PointerRNA *ptr) +static char *rna_Bone_path(const PointerRNA *ptr) { - ID *id = ptr->owner_id; - Bone *bone = (Bone *)ptr->data; + const ID *id = ptr->owner_id; + const Bone *bone = (const Bone *)ptr->data; char name_esc[sizeof(bone->name) * 2]; BLI_str_escape(name_esc, bone->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index d49bac15242..76e1e712cad 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -40,12 +40,21 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_type_items[] = { + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_BYTE_COLOR, + "BYTE_COLOR", + 0, + "Byte Color", + "RGBA color with 8-bit positive integer values"}, + {0, NULL, 0, NULL, NULL}}; + const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""}, {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, - {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with 32-bit floating-point values"}, {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, @@ -92,6 +101,16 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_color_attribute_domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, NULL, 0, NULL, NULL}}; + +const EnumPropertyItem rna_enum_attribute_curves_domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Control Point", ""}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", ""}, + {0, NULL, 0, NULL, NULL}}; + #ifdef RNA_RUNTIME # include "BLI_math.h" @@ -104,9 +123,9 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { /* Attribute */ -static char *rna_Attribute_path(PointerRNA *ptr) +static char *rna_Attribute_path(const PointerRNA *ptr) { - CustomDataLayer *layer = ptr->data; + const CustomDataLayer *layer = ptr->data; return BLI_sprintfN("attributes['%s']", layer->name); } @@ -218,6 +237,12 @@ static int rna_Attribute_domain_get(PointerRNA *ptr) return BKE_id_attribute_domain(ptr->owner_id, ptr->data); } +static bool rna_Attribute_is_internal_get(PointerRNA *ptr) +{ + const CustomDataLayer *layer = (const CustomDataLayer *)ptr->data; + return BKE_attribute_allow_procedural_access(layer->name); +} + static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -916,6 +941,12 @@ static void rna_def_attribute(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Domain", "Domain of the Attribute"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "is_internal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Attribute_is_internal_get", NULL); + RNA_def_property_ui_text( + prop, "Is Internal", "The attribute is meant for internal use by Blender"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + /* types */ rna_def_attribute_float(brna); rna_def_attribute_float_vector(brna); @@ -928,7 +959,7 @@ static void rna_def_attribute(BlenderRNA *brna) rna_def_attribute_int8(brna); } -/* Mesh/PointCloud/Hair.attributes */ +/* Mesh/PointCloud/Curves.attributes */ static void rna_def_attribute_group(BlenderRNA *brna) { StructRNA *srna; diff --git a/source/blender/makesrna/intern/rna_boid.c b/source/blender/makesrna/intern/rna_boid.c index 0818f009d1f..d65c8a1b4e3 100644 --- a/source/blender/makesrna/intern/rna_boid.c +++ b/source/blender/makesrna/intern/rna_boid.c @@ -162,9 +162,9 @@ static StructRNA *rna_BoidRule_refine(struct PointerRNA *ptr) } } -static char *rna_BoidRule_path(PointerRNA *ptr) +static char *rna_BoidRule_path(const PointerRNA *ptr) { - BoidRule *rule = (BoidRule *)ptr->data; + const BoidRule *rule = (BoidRule *)ptr->data; char name_esc[sizeof(rule->name) * 2]; BLI_str_escape(name_esc, rule->name, sizeof(name_esc)); @@ -222,16 +222,16 @@ static void rna_BoidState_active_boid_rule_index_set(struct PointerRNA *ptr, int } } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { - ID *id = ptr->owner_id; + const ID *id = ptr->owner_id; return (GS(id->name) == ID_PA); } -static char *rna_BoidSettings_path(PointerRNA *ptr) +static char *rna_BoidSettings_path(const PointerRNA *ptr) { - BoidSettings *boids = (BoidSettings *)ptr->data; + const BoidSettings *boids = (BoidSettings *)ptr->data; if (particle_id_check(ptr)) { ParticleSettings *part = (ParticleSettings *)ptr->owner_id; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 848779c49f7..46b009191ca 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -98,14 +98,14 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_INFLATE, "INFLATE", ICON_BRUSH_INFLATE, "Inflate", ""}, {SCULPT_TOOL_BLOB, "BLOB", ICON_BRUSH_BLOB, "Blob", ""}, {SCULPT_TOOL_CREASE, "CREASE", ICON_BRUSH_CREASE, "Crease", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_SMOOTH, "SMOOTH", ICON_BRUSH_SMOOTH, "Smooth", ""}, {SCULPT_TOOL_FLATTEN, "FLATTEN", ICON_BRUSH_FLATTEN, "Flatten", ""}, {SCULPT_TOOL_FILL, "FILL", ICON_BRUSH_FILL, "Fill", ""}, {SCULPT_TOOL_SCRAPE, "SCRAPE", ICON_BRUSH_SCRAPE, "Scrape", ""}, {SCULPT_TOOL_MULTIPLANE_SCRAPE, "MULTIPLANE_SCRAPE", ICON_BRUSH_SCRAPE, "Multi-plane Scrape", ""}, {SCULPT_TOOL_PINCH, "PINCH", ICON_BRUSH_PINCH, "Pinch", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_GRAB, "GRAB", ICON_BRUSH_GRAB, "Grab", ""}, {SCULPT_TOOL_ELASTIC_DEFORM, "ELASTIC_DEFORM", ICON_BRUSH_GRAB, "Elastic Deform", ""}, {SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_BRUSH_SNAKE_HOOK, "Snake Hook", ""}, @@ -115,7 +115,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_ROTATE, "ROTATE", ICON_BRUSH_ROTATE, "Rotate", ""}, {SCULPT_TOOL_SLIDE_RELAX, "TOPOLOGY", ICON_BRUSH_GRAB, "Slide Relax", ""}, {SCULPT_TOOL_BOUNDARY, "BOUNDARY", ICON_BRUSH_GRAB, "Boundary", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""}, {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, @@ -249,6 +249,7 @@ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Curves Snake Hook", ""}, {CURVES_SCULPT_TOOL_ADD, "ADD", ICON_NONE, "Add Curves", ""}, {CURVES_SCULPT_TOOL_GROW_SHRINK, "GROW_SHRINK", ICON_NONE, "Grow / Shrink Curves", ""}, + {CURVES_SCULPT_TOOL_SELECTION_PAINT, "SELECTION_PAINT", ICON_NONE, "Paint Selection", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -885,6 +886,7 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, case PAINT_MODE_SCULPT_CURVES: switch (me->curves_sculpt_tool) { case CURVES_SCULPT_TOOL_GROW_SHRINK: + case CURVES_SCULPT_TOOL_SELECTION_PAINT: return prop_direction_items; default: return DummyRNA_DEFAULT_items; @@ -934,7 +936,7 @@ static const EnumPropertyItem *rna_Brush_stroke_itemf(bContext *C, } /* Grease Pencil Drawing Brushes Settings */ -static char *rna_BrushGpencilSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_BrushGpencilSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint.brush.gpencil_settings"); } @@ -1844,6 +1846,26 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_automasking_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_STROKE); + RNA_def_property_ui_text(prop, "Auto-Masking Strokes", "Mask strokes below brush cursor"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_layer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_LAYER); + RNA_def_property_ui_text(prop, "Auto-Masking Layer", "Mask strokes using active layer"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_automasking_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL); + RNA_def_property_ui_text(prop, "Auto-Masking Material", "Mask strokes using active material"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Material */ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); @@ -1931,7 +1953,12 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) prop = RNA_def_property(srna, "add_amount", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 1, INT32_MAX); - RNA_def_property_ui_text(prop, "Add Amount", "Number of curves added by the Add brush"); + RNA_def_property_ui_text(prop, "Count", "Number of curves added by the Add brush"); + + prop = RNA_def_property(srna, "points_per_curve", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 2, INT32_MAX); + RNA_def_property_ui_text( + prop, "Points per Curve", "Number of control points in a newly added curve"); prop = RNA_def_property(srna, "scale_uniform", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM); @@ -1950,6 +1977,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Interpolate Length", "Use length of the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT); + RNA_def_property_ui_text(prop, + "Interpolate Point Count", + "Use the number of points from the curves in close proximity"); + prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); RNA_def_property_ui_text( @@ -1970,7 +2004,7 @@ static void rna_def_brush(BlenderRNA *brna) static const EnumPropertyItem prop_blend_items[] = { {IMB_BLEND_MIX, "MIX", 0, "Mix", "Use Mix blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_DARKEN, "DARKEN", 0, "Darken", "Use Darken blending mode while painting"}, {IMB_BLEND_MUL, "MUL", 0, "Multiply", "Use Multiply blending mode while painting"}, {IMB_BLEND_COLORBURN, @@ -1983,7 +2017,7 @@ static void rna_def_brush(BlenderRNA *brna) 0, "Linear Burn", "Use Linear Burn blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_LIGHTEN, "LIGHTEN", 0, "Lighten", "Use Lighten blending mode while painting"}, {IMB_BLEND_SCREEN, "SCREEN", 0, "Screen", "Use Screen blending mode while painting"}, {IMB_BLEND_COLORDODGE, @@ -1992,7 +2026,7 @@ static void rna_def_brush(BlenderRNA *brna) "Color Dodge", "Use Color Dodge blending mode while painting"}, {IMB_BLEND_ADD, "ADD", 0, "Add", "Use Add blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_OVERLAY, "OVERLAY", 0, "Overlay", "Use Overlay blending mode while painting"}, {IMB_BLEND_SOFTLIGHT, "SOFTLIGHT", @@ -2019,7 +2053,7 @@ static void rna_def_brush(BlenderRNA *brna) 0, "Pin Light", "Use Pin Light blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_DIFFERENCE, "DIFFERENCE", 0, @@ -2031,7 +2065,7 @@ static void rna_def_brush(BlenderRNA *brna) "Exclusion", "Use Exclusion blending mode while painting"}, {IMB_BLEND_SUB, "SUB", 0, "Subtract", "Use Subtract blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_HUE, "HUE", 0, "Hue", "Use Hue blending mode while painting"}, {IMB_BLEND_SATURATION, "SATURATION", @@ -2040,7 +2074,7 @@ static void rna_def_brush(BlenderRNA *brna) "Use Saturation blending mode while painting"}, {IMB_BLEND_COLOR, "COLOR", 0, "Color", "Use Color blending mode while painting"}, {IMB_BLEND_LUMINOSITY, "LUMINOSITY", 0, "Value", "Use Value blending mode while painting"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {IMB_BLEND_ERASE_ALPHA, "ERASE_ALPHA", 0, "Erase Alpha", "Erase alpha while painting"}, {IMB_BLEND_ADD_ALPHA, "ADD_ALPHA", 0, "Add Alpha", "Add alpha while painting"}, {0, NULL, 0, NULL, NULL}, diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index f535cdcee96..dcf0647392e 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -111,13 +111,70 @@ static void rna_Camera_background_images_clear(Camera *cam) WM_main_add_notifier(NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, cam); } +static char *rna_Camera_background_image_path(const PointerRNA *ptr) +{ + const CameraBGImage *bgpic = ptr->data; + Camera *camera = (Camera *)ptr->owner_id; + + const int bgpic_index = BLI_findindex(&camera->bg_images, bgpic); + + if (bgpic_index >= 0) { + return BLI_sprintfN("background_images[%d]", bgpic_index); + } + + return NULL; +} + +static bool rna_Camera_background_images_override_apply(Main *bmain, + PointerRNA *ptr_dst, + PointerRNA *ptr_src, + PointerRNA *UNUSED(ptr_storage), + PropertyRNA *prop_dst, + PropertyRNA *UNUSED(prop_src), + PropertyRNA *UNUSED(prop_storage), + const int UNUSED(len_dst), + const int UNUSED(len_src), + const int UNUSED(len_storage), + PointerRNA *UNUSED(ptr_item_dst), + PointerRNA *UNUSED(ptr_item_src), + PointerRNA *UNUSED(ptr_item_storage), + IDOverrideLibraryPropertyOperation *opop) +{ + BLI_assert_msg(opop->operation == IDOVERRIDE_LIBRARY_OP_INSERT_AFTER, + "Unsupported RNA override operation on background images collection"); + + Camera *cam_dst = (Camera *)ptr_dst->owner_id; + Camera *cam_src = (Camera *)ptr_src->owner_id; + + /* Remember that insertion operations are defined and stored in correct order, which means that + * even if we insert several items in a row, we always insert first one, then second one, etc. + * So we should always find 'anchor' constraint in both _src *and* _dst. */ + CameraBGImage *bgpic_anchor = BLI_findlink(&cam_dst->bg_images, opop->subitem_reference_index); + + /* If `bgpic_anchor` is NULL, `bgpic_src` will be inserted in first position. */ + CameraBGImage *bgpic_src = BLI_findlink(&cam_src->bg_images, opop->subitem_local_index); + + if (bgpic_src == NULL) { + BLI_assert(bgpic_src != NULL); + return false; + } + + CameraBGImage *bgpic_dst = BKE_camera_background_image_copy(bgpic_src, 0); + + /* This handles NULL anchor as expected by adding at head of list. */ + BLI_insertlinkafter(&cam_dst->bg_images, bgpic_anchor, bgpic_dst); + + RNA_property_update_main(bmain, NULL, ptr_dst, prop_dst); + return true; +} + static void rna_Camera_dof_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { SEQ_relations_invalidate_scene_strips(bmain, scene); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } -char *rna_CameraDOFSettings_path(PointerRNA *ptr) +char *rna_CameraDOFSettings_path(const PointerRNA *ptr) { /* if there is ID-data, resolve the path using the index instead of by name, * since the name used is the name of the texture assigned, but the texture @@ -179,6 +236,17 @@ static void rna_def_camera_background_image(BlenderRNA *brna) RNA_def_struct_sdna(srna, "CameraBGImage"); RNA_def_struct_ui_text( srna, "Background Image", "Image and settings for display in the 3D View background"); + RNA_def_struct_path_func(srna, "rna_Camera_background_image_path"); + + prop = RNA_def_boolean(srna, + "is_override_data", + false, + "Override Background Image", + "In a local override camera, whether this background image comes from " + "the linked reference camera, or is local to the override"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_negative_sdna( + prop, NULL, "flag", CAM_BGIMG_FLAG_OVERRIDE_LIBRARY_LOCAL); RNA_define_lib_overridable(true); @@ -736,6 +804,8 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "bg_images", NULL); RNA_def_property_struct_type(prop, "CameraBackgroundImage"); RNA_def_property_ui_text(prop, "Background Images", "List of background images"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_LIBRARY_INSERTION | PROPOVERRIDE_NO_PROP_NAME); + RNA_def_property_override_funcs(prop, NULL, NULL, "rna_Camera_background_images_override_apply"); RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL); RNA_define_lib_overridable(false); diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 3ad901e5397..e45a1a3cc33 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -436,10 +436,10 @@ static void rna_ClothSettings_gravity_set(PointerRNA *ptr, const float *values) sim->gravity[2] = values[2]; } -static char *rna_ClothSettings_path(PointerRNA *ptr) +static char *rna_ClothSettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); if (md) { char name_esc[sizeof(md->name) * 2]; @@ -451,10 +451,10 @@ static char *rna_ClothSettings_path(PointerRNA *ptr) } } -static char *rna_ClothCollisionSettings_path(PointerRNA *ptr) +static char *rna_ClothCollisionSettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Cloth); if (md) { char name_esc[sizeof(md->name) * 2]; diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 840674c7bc6..92cdcc6d781 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -157,7 +157,7 @@ static void rna_CurveMapping_clipmaxy_range( *max = 100.0f; } -static char *rna_ColorRamp_path(PointerRNA *ptr) +static char *rna_ColorRamp_path(const PointerRNA *ptr) { char *path = NULL; @@ -208,7 +208,7 @@ static char *rna_ColorRamp_path(PointerRNA *ptr) return path; } -static char *rna_ColorRampElement_path(PointerRNA *ptr) +static char *rna_ColorRampElement_path(const PointerRNA *ptr) { PointerRNA ramp_ptr; PropertyRNA *prop; @@ -438,7 +438,7 @@ static void rna_ColorManagedDisplaySettings_display_device_update(Main *bmain, } } -static char *rna_ColorManagedDisplaySettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedDisplaySettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display_settings"); } @@ -526,7 +526,7 @@ static void rna_ColorManagedViewSettings_use_curves_set(PointerRNA *ptr, bool va } } -static char *rna_ColorManagedViewSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedViewSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("view_settings"); } @@ -662,12 +662,12 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, } } -static char *rna_ColorManagedSequencerColorspaceSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedSequencerColorspaceSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("sequencer_colorspace_settings"); } -static char *rna_ColorManagedInputColorspaceSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ColorManagedInputColorspaceSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("colorspace_settings"); } diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 63d8876ec8b..24f226eb6e6 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -29,11 +29,12 @@ /* please keep the names in sync with constraint.c */ const EnumPropertyItem rna_enum_constraint_type_items[] = { - {0, "", 0, N_("Motion Tracking"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Motion Tracking"), NULL), {CONSTRAINT_TYPE_CAMERASOLVER, "CAMERA_SOLVER", ICON_CON_CAMERASOLVER, "Camera Solver", ""}, {CONSTRAINT_TYPE_FOLLOWTRACK, "FOLLOW_TRACK", ICON_CON_FOLLOWTRACK, "Follow Track", ""}, {CONSTRAINT_TYPE_OBJECTSOLVER, "OBJECT_SOLVER", ICON_CON_OBJECTSOLVER, "Object Solver", ""}, - {0, "", 0, N_("Transform"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Transform"), NULL), {CONSTRAINT_TYPE_LOCLIKE, "COPY_LOCATION", ICON_CON_LOCLIKE, @@ -91,7 +92,8 @@ const EnumPropertyItem rna_enum_constraint_type_items[] = { ICON_CON_TRANSFORM_CACHE, "Transform Cache", "Look up the transformation matrix from an external file"}, - {0, "", 0, N_("Tracking"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Tracking"), NULL), {CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CON_CLAMPTO, @@ -127,7 +129,8 @@ const EnumPropertyItem rna_enum_constraint_type_items[] = { ICON_CON_TRACKTO, "Track To", "Legacy tracking constraint prone to twisting artifacts"}, - {0, "", 0, N_("Relationship"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Relationship"), NULL), {CONSTRAINT_TYPE_ACTION, "ACTION", ICON_ACTION, @@ -192,7 +195,7 @@ static const EnumPropertyItem target_space_pchan_items[] = { "Custom Space", "The transformation of the target is evaluated relative to a custom object/bone/vertex " "group"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {CONSTRAINT_SPACE_POSE, "POSE", 0, @@ -233,7 +236,7 @@ static const EnumPropertyItem owner_space_pchan_items[] = { 0, "Custom Space", "The constraint is applied in local space of a custom object/bone/vertex group"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {CONSTRAINT_SPACE_POSE, "POSE", 0, @@ -440,7 +443,7 @@ static char *rna_Constraint_do_compute_path(Object *ob, bConstraint *con) } } -static char *rna_Constraint_path(PointerRNA *ptr) +static char *rna_Constraint_path(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraint *con = ptr->data; @@ -448,7 +451,7 @@ static char *rna_Constraint_path(PointerRNA *ptr) return rna_Constraint_do_compute_path(ob, con); } -static bConstraint *rna_constraint_from_target(PointerRNA *ptr) +static bConstraint *rna_constraint_from_target(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraintTarget *tgt = ptr->data; @@ -456,7 +459,7 @@ static bConstraint *rna_constraint_from_target(PointerRNA *ptr) return BKE_constraint_find_from_target(ob, tgt, NULL); } -static char *rna_ConstraintTarget_path(PointerRNA *ptr) +static char *rna_ConstraintTarget_path(const PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; bConstraintTarget *tgt = ptr->data; @@ -691,11 +694,11 @@ static void rna_ActionConstraint_minmax_range( } } -static int rna_SplineIKConstraint_joint_bindings_get_length(PointerRNA *ptr, +static int rna_SplineIKConstraint_joint_bindings_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - bConstraint *con = (bConstraint *)ptr->data; - bSplineIKConstraint *ikData = (bSplineIKConstraint *)con->data; + const bConstraint *con = (bConstraint *)ptr->data; + const bSplineIKConstraint *ikData = (bSplineIKConstraint *)con->data; if (ikData) { length[0] = ikData->numpoints; @@ -1623,7 +1626,7 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna) 0, "Replace", "Replace the original transformation with copied"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {TRANSLIKE_MIX_BEFORE_FULL, "BEFORE_FULL", 0, @@ -1644,7 +1647,7 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna) "Before Original (Split Channels)", "Apply copied transformation before original, handling location, rotation and scale " "separately, similar to a sequence of three Copy constraints"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {TRANSLIKE_MIX_AFTER_FULL, "AFTER_FULL", 0, @@ -1782,7 +1785,7 @@ static void rna_def_constraint_action(BlenderRNA *brna) "Before Original (Split Channels)", "Apply the action channels before the original transformation, handling location, rotation " "and scale separately"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {ACTCON_MIX_AFTER_FULL, "AFTER_FULL", 0, diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index fb911725836..fff3f479a3f 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -66,8 +66,8 @@ const EnumPropertyItem rna_enum_keyframe_handle_type_items[] = { * Changes here will likely apply there too. */ const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = { - /* interpolation */ - {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, + /* Interpolation. */ + RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"), {BEZT_IPO_CONST, "CONSTANT", ICON_IPO_CONSTANT, @@ -84,13 +84,10 @@ const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = { "Bezier", "Smooth interpolation between A and B, with some control over curve shape"}, - /* easing */ - {0, - "", - 0, - N_("Easing (by strength)"), - "Predefined inertial transitions, useful for motion graphics (from least to most " - "''dramatic'')"}, + /* Easing. */ + RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"), + "Predefined inertial transitions, useful for motion graphics " + "(from least to most \"dramatic\")"), {BEZT_IPO_SINE, "SINE", ICON_IPO_SINE, @@ -107,7 +104,7 @@ const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = { "Circular", "Circular easing (strongest and most dynamic)"}, - {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"), {BEZT_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"}, {BEZT_IPO_BOUNCE, "BOUNCE", @@ -770,7 +767,7 @@ static void rna_Curve_active_spline_set(PointerRNA *ptr, } } -static char *rna_Curve_spline_path(PointerRNA *ptr) +static char *rna_Curve_spline_path(const PointerRNA *ptr) { Curve *cu = (Curve *)ptr->owner_id; ListBase *nubase = BKE_curve_nurbs_get(cu); @@ -786,7 +783,7 @@ static char *rna_Curve_spline_path(PointerRNA *ptr) } /* use for both bezier and nurbs */ -static char *rna_Curve_spline_point_path(PointerRNA *ptr) +static char *rna_Curve_spline_point_path(const PointerRNA *ptr) { Curve *cu = (Curve *)ptr->owner_id; Nurb *nu; @@ -808,10 +805,10 @@ static char *rna_Curve_spline_point_path(PointerRNA *ptr) } } -static char *rna_TextBox_path(PointerRNA *ptr) +static char *rna_TextBox_path(const PointerRNA *ptr) { - Curve *cu = (Curve *)ptr->owner_id; - TextBox *tb = ptr->data; + const Curve *cu = (Curve *)ptr->owner_id; + const TextBox *tb = ptr->data; int index = (int)(tb - cu->tb); if (index >= 0 && index < cu->totbox) { diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 7a1a368551f..caefd2f45ff 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -30,7 +30,7 @@ # include "WM_api.h" # include "WM_types.h" -static Curves *rna_curves(PointerRNA *ptr) +static Curves *rna_curves(const PointerRNA *ptr) { return (Curves *)ptr->owner_id; } @@ -38,7 +38,7 @@ static Curves *rna_curves(PointerRNA *ptr) static int rna_Curves_curve_offset_data_length(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - return curves->geometry.curve_size + 1; + return curves->geometry.curve_num + 1; } static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -47,18 +47,23 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, rna_iterator_array_begin(iter, (void *)curves->geometry.curve_offsets, sizeof(int), - curves->geometry.curve_size + 1, + curves->geometry.curve_num + 1, false, NULL); } -static int rna_CurvePoint_index_get(PointerRNA *ptr) +static int rna_CurvePoint_index_get_const(const PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); const float(*co)[3] = ptr->data; return (int)(co - curves->geometry.position); } +static int rna_CurvePoint_index_get(PointerRNA *ptr) +{ + return rna_CurvePoint_index_get_const(ptr); +} + static void rna_CurvePoint_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -89,20 +94,25 @@ static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value) curves->geometry.radius[co - curves->geometry.position] = value; } -static char *rna_CurvePoint_path(PointerRNA *ptr) +static char *rna_CurvePoint_path(const PointerRNA *ptr) { - return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get(ptr)); + return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get_const(ptr)); } -static int rna_CurveSlice_index_get(PointerRNA *ptr) +static int rna_CurveSlice_index_get_const(const PointerRNA *ptr) { Curves *curves = rna_curves(ptr); return (int)((int *)ptr->data - curves->geometry.curve_offsets); } -static char *rna_CurveSlice_path(PointerRNA *ptr) +static int rna_CurveSlice_index_get(PointerRNA *ptr) { - return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get(ptr)); + return rna_CurveSlice_index_get_const(ptr); +} + +static char *rna_CurveSlice_path(const PointerRNA *ptr) +{ + return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get_const(ptr)); } static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -133,14 +143,22 @@ static void rna_Curves_update_data(struct Main *UNUSED(bmain), PointerRNA *ptr) { ID *id = ptr->owner_id; - - /* cheating way for importers to avoid slow updates */ + /* Avoid updates for importers creating curves. */ if (id->us > 0) { DEG_id_tag_update(id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, id); } } +void rna_Curves_update_draw(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + /* Avoid updates for importers creating curves. */ + if (id->us > 0) { + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + } +} + #else static void rna_def_curves_point(BlenderRNA *brna) @@ -222,7 +240,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.curve_offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_num"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -230,7 +248,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); RNA_define_verify_sdna(1); @@ -239,7 +257,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); @@ -274,6 +292,34 @@ static void rna_def_curves(BlenderRNA *brna) 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); + /* Symmetry. */ + prop = RNA_def_property(srna, "use_mirror_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_X); + RNA_def_property_ui_text(prop, "X", "Enable symmetry in the X axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Y); + RNA_def_property_ui_text(prop, "Y", "Enable symmetry in the Y axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + + prop = RNA_def_property(srna, "use_mirror_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "symmetry", CURVES_SYMMETRY_Z); + RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis"); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + + prop = RNA_def_property(srna, "selection_domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_curves_domain_items); + RNA_def_property_ui_text(prop, "Selection Domain", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Curves_update_data"); + + prop = RNA_def_property(srna, "use_sculpt_selection", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", CV_SCULPT_SELECTION_ENABLED); + RNA_def_property_ui_text(prop, "Use Sculpt Selection", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Curves_update_draw"); + /* 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 8e2b9c1d937..9d26797aa88 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -208,7 +208,7 @@ static int DNA_struct_find_nr_wrapper(const struct SDNA *sdna, const char *struc struct_name = DNA_struct_rename_legacy_hack_static_from_alias(struct_name); #ifdef RNA_RUNTIME /* We may support this at some point but for now we don't. */ - BLI_assert(0); + BLI_assert_unreachable(); #else struct_name = BLI_ghash_lookup_default( g_version_data.struct_map_static_from_alias, struct_name, (void *)struct_name); @@ -3316,6 +3316,33 @@ void RNA_def_property_string_funcs(PropertyRNA *prop, } } +void RNA_def_property_string_search_func(PropertyRNA *prop, + const char *search, + const eStringPropertySearchFlag search_flag) +{ + StructRNA *srna = DefRNA.laststruct; + + if (!DefRNA.preprocess) { + CLOG_ERROR(&LOG, "only during preprocessing."); + return; + } + + switch (prop->type) { + case PROP_STRING: { + StringPropertyRNA *sprop = (StringPropertyRNA *)prop; + sprop->search = (StringPropertySearchFunc)search; + if (search != NULL) { + sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED; + } + break; + } + default: + CLOG_ERROR(&LOG, "\"%s.%s\", type is not string.", srna->identifier, prop->identifier); + DefRNA.error = true; + break; + } +} + void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, StringPropertyGetFunc getfunc, StringPropertyLengthFunc lengthfunc, @@ -3343,6 +3370,18 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop, } } +void RNA_def_property_string_search_func_runtime(PropertyRNA *prop, + StringPropertySearchFunc search_fn, + const eStringPropertySearchFlag search_flag) +{ + StringPropertyRNA *sprop = (StringPropertyRNA *)prop; + + sprop->search = search_fn; + if (search_fn != NULL) { + sprop->search_flag = search_flag | PROP_STRING_SEARCH_SUPPORTED; + } +} + void RNA_def_property_pointer_funcs( PropertyRNA *prop, const char *get, const char *set, const char *type_fn, const char *poll) { @@ -4409,7 +4448,7 @@ void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropert void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem) { - static const EnumPropertyItem sepr = {0, "", 0, NULL, NULL}; + static const EnumPropertyItem sepr = RNA_ENUM_ITEM_SEPR; RNA_enum_item_add(items, totitem, &sepr); } diff --git a/source/blender/makesrna/intern/rna_dynamicpaint.c b/source/blender/makesrna/intern/rna_dynamicpaint.c index ed6d4996c1e..6f9fe3741f7 100644 --- a/source/blender/makesrna/intern/rna_dynamicpaint.c +++ b/source/blender/makesrna/intern/rna_dynamicpaint.c @@ -37,30 +37,30 @@ const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[] = { # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" -static char *rna_DynamicPaintCanvasSettings_path(PointerRNA *ptr) +static char *rna_DynamicPaintCanvasSettings_path(const PointerRNA *ptr) { - DynamicPaintCanvasSettings *settings = (DynamicPaintCanvasSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->pmd; + const DynamicPaintCanvasSettings *settings = (DynamicPaintCanvasSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->pmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].canvas_settings", name_esc); } -static char *rna_DynamicPaintBrushSettings_path(PointerRNA *ptr) +static char *rna_DynamicPaintBrushSettings_path(const PointerRNA *ptr) { - DynamicPaintBrushSettings *settings = (DynamicPaintBrushSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->pmd; + const DynamicPaintBrushSettings *settings = (DynamicPaintBrushSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->pmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].brush_settings", name_esc); } -static char *rna_DynamicPaintSurface_path(PointerRNA *ptr) +static char *rna_DynamicPaintSurface_path(const PointerRNA *ptr) { - DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data; - ModifierData *md = (ModifierData *)surface->canvas->pmd; + const DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data; + const ModifierData *md = (ModifierData *)surface->canvas->pmd; char name_esc[sizeof(md->name) * 2]; char name_esc_surface[sizeof(surface->name) * 2]; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index af14a169d68..ac8aebd2fdd 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -786,11 +786,11 @@ static void rna_FModifier_active_update(Main *bmain, Scene *scene, PointerRNA *p rna_FModifier_update(bmain, scene, ptr); } -static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr, +static int rna_FModifierGenerator_coefficients_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FModifier *fcm = (FModifier *)ptr->data; - FMod_Generator *gen = fcm->data; + const FModifier *fcm = (FModifier *)ptr->data; + const FMod_Generator *gen = fcm->data; if (gen) { length[0] = gen->arraysize; @@ -1776,12 +1776,12 @@ static void rna_def_drivertarget(BlenderRNA *brna) {DTAR_TRANSCHAN_LOCX, "LOC_X", 0, "X Location", ""}, {DTAR_TRANSCHAN_LOCY, "LOC_Y", 0, "Y Location", ""}, {DTAR_TRANSCHAN_LOCZ, "LOC_Z", 0, "Z Location", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {DTAR_TRANSCHAN_ROTX, "ROT_X", 0, "X Rotation", ""}, {DTAR_TRANSCHAN_ROTY, "ROT_Y", 0, "Y Rotation", ""}, {DTAR_TRANSCHAN_ROTZ, "ROT_Z", 0, "Z Rotation", ""}, {DTAR_TRANSCHAN_ROTW, "ROT_W", 0, "W Rotation", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {DTAR_TRANSCHAN_SCALEX, "SCALE_X", 0, "X Scale", ""}, {DTAR_TRANSCHAN_SCALEY, "SCALE_Y", 0, "Y Scale", ""}, {DTAR_TRANSCHAN_SCALEZ, "SCALE_Z", 0, "Z Scale", ""}, diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index e0ec146a248..3b22ae9d40f 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -857,30 +857,30 @@ static void rna_Fluid_domaintype_set(struct PointerRNA *ptr, int value) BKE_fluid_fields_sanitize(settings); } -static char *rna_FluidDomainSettings_path(PointerRNA *ptr) +static char *rna_FluidDomainSettings_path(const PointerRNA *ptr) { - FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].domain_settings", name_esc); } -static char *rna_FluidFlowSettings_path(PointerRNA *ptr) +static char *rna_FluidFlowSettings_path(const PointerRNA *ptr) { - FluidFlowSettings *settings = (FluidFlowSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidFlowSettings *settings = (FluidFlowSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].flow_settings", name_esc); } -static char *rna_FluidEffectorSettings_path(PointerRNA *ptr) +static char *rna_FluidEffectorSettings_path(const PointerRNA *ptr) { - FluidEffectorSettings *settings = (FluidEffectorSettings *)ptr->data; - ModifierData *md = (ModifierData *)settings->fmd; + const FluidEffectorSettings *settings = (FluidEffectorSettings *)ptr->data; + const ModifierData *md = (ModifierData *)settings->fmd; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); @@ -893,9 +893,10 @@ static char *rna_FluidEffectorSettings_path(PointerRNA *ptr) # ifdef WITH_FLUID -static int rna_FluidModifier_grid_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_FluidModifier_grid_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *density = NULL; int size = 0; @@ -918,7 +919,7 @@ static int rna_FluidModifier_grid_get_length(PointerRNA *ptr, int length[RNA_MAX return length[0]; } -static int rna_FluidModifier_color_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_color_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { rna_FluidModifier_grid_get_length(ptr, length); @@ -927,10 +928,10 @@ static int rna_FluidModifier_color_grid_get_length(PointerRNA *ptr, return length[0]; } -static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_velocity_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *vx = NULL; float *vy = NULL; float *vz = NULL; @@ -948,10 +949,10 @@ static int rna_FluidModifier_velocity_grid_get_length(PointerRNA *ptr, return length[0]; } -static int rna_FluidModifier_heat_grid_get_length(PointerRNA *ptr, +static int rna_FluidModifier_heat_grid_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; + const FluidDomainSettings *fds = (FluidDomainSettings *)ptr->data; float *heat = NULL; int size = 0; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 9a9b6d582e5..6854ce37c94 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -307,7 +307,7 @@ bool rna_GPencil_datablocks_obdata_poll(PointerRNA *UNUSED(ptr), const PointerRN return (gpd->flag & GP_DATA_ANNOTATIONS) == 0; } -static char *rna_GPencilLayer_path(PointerRNA *ptr) +static char *rna_GPencilLayer_path(const PointerRNA *ptr) { bGPDlayer *gpl = (bGPDlayer *)ptr->data; char name_esc[sizeof(gpl->info) * 2]; @@ -407,7 +407,7 @@ static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value) } } -static char *rna_GPencilLayerMask_path(PointerRNA *ptr) +static char *rna_GPencilLayerMask_path(const PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->owner_id; bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); @@ -1122,7 +1122,7 @@ static void rna_GPencil_clear(bGPdata *gpd) WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -static char *rna_GreasePencilGrid_path(PointerRNA *UNUSED(ptr)) +static char *rna_GreasePencilGrid_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grid"); } diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index be2ac6b12be..c3d5819caa3 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -46,7 +46,7 @@ #include "WM_types.h" const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { - {0, "", 0, N_("Modify"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Modify"), NULL), {eGpencilModifierType_Texture, "GP_TEXTURE", ICON_MOD_UVPROJECT, @@ -63,7 +63,8 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_VERTEX_WEIGHT, "Vertex Weight Proximity", "Generate Vertex Weights base on distance to object"}, - {0, "", 0, N_("Generate"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Generate"), NULL), {eGpencilModifierType_Array, "GP_ARRAY", ICON_MOD_ARRAY, @@ -114,7 +115,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_SUBSURF, "Subdivide", "Subdivide stroke adding more control points"}, - {0, "", 0, N_("Deform"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Deform"), NULL), {eGpencilModifierType_Armature, "GP_ARMATURE", ICON_MOD_ARMATURE, @@ -147,7 +148,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_THICKNESS, "Thickness", "Change stroke thickness"}, - {0, "", 0, N_("Color"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Color"), NULL), {eGpencilModifierType_Color, "GP_COLOR", ICON_MOD_HUE_SATURATION, @@ -195,6 +196,7 @@ static const EnumPropertyItem rna_enum_time_mode_items[] = { {GP_TIME_MODE_NORMAL, "NORMAL", 0, "Regular", "Apply offset in usual animation direction"}, {GP_TIME_MODE_REVERSE, "REVERSE", 0, "Reverse", "Apply offset in reverse animation direction"}, {GP_TIME_MODE_FIX, "FIX", 0, "Fixed Frame", "Keep frame and do not change with time"}, + {GP_TIME_MODE_PINGPONG, "PINGPONG", 0, "Ping Pong", "Loop back and forth"}, {0, NULL, 0, NULL, NULL}, }; @@ -232,6 +234,11 @@ static const EnumPropertyItem gpencil_envelope_mode_items[] = { "Add fill segments to create the envelope. Don't keep the original stroke"}, {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem modifier_noise_random_mode_items[] = { + {GP_NOISE_RANDOM_STEP, "STEP", 0, "Steps", "Randomize every number of frames"}, + {GP_NOISE_RANDOM_KEYFRAME, "KEYFRAME", 0, "Keyframes", "Randomize on keyframes only"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -335,9 +342,9 @@ static void rna_GpencilModifier_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "grease_pencil_modifiers", oldname, gmd->name); } -static char *rna_GpencilModifier_path(PointerRNA *ptr) +static char *rna_GpencilModifier_path(const PointerRNA *ptr) { - GpencilModifierData *gmd = ptr->data; + const GpencilModifierData *gmd = ptr->data; char name_esc[sizeof(gmd->name) * 2]; BLI_str_escape(name_esc, gmd->name, sizeof(name_esc)); @@ -746,11 +753,11 @@ static void rna_GpencilDash_segments_begin(CollectionPropertyIterator *iter, Poi iter, dmd->segments, sizeof(DashGpencilModifierSegment), dmd->segments_len, false, NULL); } -static char *rna_DashGpencilModifierSegment_path(PointerRNA *ptr) +static char *rna_DashGpencilModifierSegment_path(const PointerRNA *ptr) { - DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; + const DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; - DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; + const DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; BLI_assert(dmd != NULL); @@ -924,8 +931,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "step"); RNA_def_property_range(prop, 1, 100); - RNA_def_property_ui_text( - prop, "Step", "Number of frames before recalculate random values again"); + RNA_def_property_ui_text(prop, "Step", "Number of frames between randomization steps"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); @@ -959,6 +965,12 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "random_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "noise_mode"); + RNA_def_property_enum_items(prop, modifier_noise_random_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Where to perform randomization"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } @@ -3259,12 +3271,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 30.0f); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES); - RNA_def_property_ui_text( - prop, "Remove Doubles", "Remove doubles from the source geometry before generating stokes"); - RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_loose_as_contour", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_LOOSE_AS_CONTOUR); RNA_def_property_ui_text(prop, "Loose As Contour", "Loose edges will have contour type"); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index bd3b03add95..269ebe1581f 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -175,7 +175,7 @@ static void rna_ImageUser_relations_update(Main *bmain, Scene *scene, PointerRNA DEG_relations_tag_update(bmain); } -static char *rna_ImageUser_path(PointerRNA *ptr) +static char *rna_ImageUser_path(const PointerRNA *ptr) { if (ptr->owner_id) { /* ImageUser *iuser = ptr->data; */ @@ -397,7 +397,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } @@ -446,7 +446,7 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr) return duration; } -static int rna_Image_pixels_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_Image_pixels_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { Image *ima = (Image *)ptr->owner_id; ImBuf *ibuf; @@ -737,6 +737,16 @@ static void rna_def_image_packed_files(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "filepath"); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "view", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "view"); + RNA_def_property_ui_text(prop, "View Index", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "tile_number", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "tile_number"); + RNA_def_property_ui_text(prop, "Tile Number", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_api_image_packed_file(srna); } diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 29f639fbe65..46bb0df5c11 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -26,6 +26,7 @@ # include "BKE_image.h" # include "BKE_image_format.h" +# include "BKE_image_save.h" # include "BKE_main.h" # include "BKE_scene.h" # include <errno.h> @@ -50,77 +51,39 @@ static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, Report static void rna_Image_save_render( Image *image, bContext *C, ReportList *reports, const char *path, Scene *scene) { - ImBuf *ibuf; + Main *bmain = CTX_data_main(C); if (scene == NULL) { scene = CTX_data_scene(C); } - if (scene) { - ImageUser iuser = {NULL}; - void *lock; + ImageSaveOptions opts; - iuser.scene = scene; + if (BKE_image_save_options_init(&opts, bmain, scene, image, NULL, false, true)) { + opts.save_copy = true; + STRNCPY(opts.filepath, path); - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL) { - BKE_report(reports, RPT_ERROR, "Could not acquire buffer from image"); - } - else { - ImBuf *write_ibuf; - - ImageFormatData image_format; - BKE_image_format_init_for_write(&image_format, scene, NULL); - - write_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &image_format); - - write_ibuf->planes = image_format.planes; - write_ibuf->dither = scene->r.dither_intensity; - - if (!BKE_imbuf_write(write_ibuf, path, &image_format)) { - BKE_reportf(reports, RPT_ERROR, "Could not write image: %s, '%s'", strerror(errno), path); - } - - if (write_ibuf != ibuf) { - IMB_freeImBuf(write_ibuf); - } - - BKE_image_format_free(&image_format); + if (!BKE_image_save(reports, bmain, image, NULL, &opts)) { + BKE_reportf( + reports, RPT_ERROR, "Image '%s' could not be saved to '%s'", image->id.name + 2, path); } - - BKE_image_release_ibuf(image, ibuf, lock); } else { - BKE_report(reports, RPT_ERROR, "Scene not in context, could not get save parameters"); + BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2); } + + BKE_image_save_options_free(&opts); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); } static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *reports) { - void *lock; - - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - if (ibuf) { - char filepath[FILE_MAX]; - BLI_strncpy(filepath, image->filepath, sizeof(filepath)); - BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &image->id)); - - /* NOTE: we purposefully ignore packed files here, - * developers need to explicitly write them via 'packed_files' */ + Scene *scene = CTX_data_scene(C); + ImageSaveOptions opts; - if (IMB_saveiff(ibuf, filepath, ibuf->flags)) { - image->type = IMA_TYPE_IMAGE; - - if (image->source == IMA_SRC_GENERATED) { - image->source = IMA_SRC_FILE; - } - - IMB_colormanagement_colorspace_from_ibuf_ftype(&image->colorspace_settings, ibuf); - - ibuf->userflags &= ~IB_BITMAPDIRTY; - } - else { + if (BKE_image_save_options_init(&opts, bmain, scene, image, NULL, false, false)) { + if (!BKE_image_save(reports, bmain, image, NULL, &opts)) { BKE_reportf(reports, RPT_ERROR, "Image '%s' could not be saved to '%s'", @@ -132,7 +95,8 @@ static void rna_Image_save(Image *image, Main *bmain, bContext *C, ReportList *r BKE_reportf(reports, RPT_ERROR, "Image '%s' does not have any image data", image->id.name + 2); } - BKE_image_release_ibuf(image, ibuf, lock); + BKE_image_save_options_free(&opts); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, image); } @@ -161,9 +125,8 @@ static void rna_Image_unpack(Image *image, Main *bmain, ReportList *reports, int if (!BKE_image_has_packedfile(image)) { BKE_report(reports, RPT_ERROR, "Image not packed"); } - else if (BKE_image_has_multiple_ibufs(image)) { - BKE_report( - reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported"); + else if (ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + BKE_report(reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return; } else { @@ -234,7 +197,7 @@ static int rna_Image_gl_touch( BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL] == NULL) { + if (image->gputexture[TEXTARGET_2D][0] == NULL) { error = rna_Image_gl_load(image, reports, frame, layer_index, pass_index); } diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index bf4eec433c4..058c63f640a 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -373,7 +373,7 @@ void rna_ViewLayer_active_lightgroup_index_set(PointerRNA *ptr, int value); * `rna_path_buffer_size` should be at least `sizeof(ViewLayer.name) * 3`. * \return actual length of the generated RNA path. */ -size_t rna_ViewLayer_path_buffer_get(struct ViewLayer *view_layer, +size_t rna_ViewLayer_path_buffer_get(const struct ViewLayer *view_layer, char *r_rna_path, const size_t rna_path_buffer_size); @@ -402,8 +402,8 @@ bool rna_GPencil_datablocks_annotations_poll(struct PointerRNA *ptr, const struct PointerRNA value); bool rna_GPencil_datablocks_obdata_poll(struct PointerRNA *ptr, const struct PointerRNA value); -char *rna_TextureSlot_path(struct PointerRNA *ptr); -char *rna_Node_ImageUser_path(struct PointerRNA *ptr); +char *rna_TextureSlot_path(const struct PointerRNA *ptr); +char *rna_Node_ImageUser_path(const struct PointerRNA *ptr); /* Set U.is_dirty and redraw. */ diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 4db438f04b4..4f88959b5ba 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -50,9 +50,10 @@ typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info); typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index); typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr); typedef struct StructRNA *(*StructRefineFunc)(struct PointerRNA *ptr); -typedef char *(*StructPathFunc)(struct PointerRNA *ptr); +typedef char *(*StructPathFunc)(const struct PointerRNA *ptr); -typedef int (*PropArrayLengthGetFunc)(struct PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]); +typedef int (*PropArrayLengthGetFunc)(const struct PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]); typedef bool (*PropBooleanGetFunc)(struct PointerRNA *ptr); typedef void (*PropBooleanSetFunc)(struct PointerRNA *ptr, bool value); typedef void (*PropBooleanArrayGetFunc)(struct PointerRNA *ptr, bool *values); @@ -439,6 +440,15 @@ typedef struct StringPropertyRNA { PropStringLengthFuncEx length_ex; PropStringSetFuncEx set_ex; + /** + * Optional callback to list candidates for a string. + * This is only for use as suggestions in UI, other values may be assigned. + * + * \note The callback type is public, hence the difference in naming convention. + */ + StringPropertySearchFunc search; + eStringPropertySearchFlag search_flag; + int maxlength; /* includes string terminator! */ const char *defaultvalue; diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 50b25157989..b16d127a643 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -26,6 +26,14 @@ #include "rna_internal.h" +const EnumPropertyItem rna_enum_keyblock_type_items[] = { + {KEY_LINEAR, "KEY_LINEAR", 0, "Linear", ""}, + {KEY_CARDINAL, "KEY_CARDINAL", 0, "Cardinal", ""}, + {KEY_CATMULL_ROM, "KEY_CATMULL_ROM", 0, "Catmull-Rom", ""}, + {KEY_BSPLINE, "KEY_BSPLINE", 0, "BSpline", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include <stddef.h> @@ -159,7 +167,7 @@ static void rna_ShapeKey_slider_max_set(PointerRNA *ptr, float value) * such case looks rather unlikely - and not worth adding some kind of caching in key-blocks. */ -static Mesh *rna_KeyBlock_normals_get_mesh(PointerRNA *ptr, ID *id) +static Mesh *rna_KeyBlock_normals_get_mesh(const PointerRNA *ptr, ID *id) { Key *key = rna_ShapeKey_find_key((id == NULL && ptr != NULL) ? ptr->owner_id : id); id = key ? key->from : NULL; @@ -182,9 +190,10 @@ static Mesh *rna_KeyBlock_normals_get_mesh(PointerRNA *ptr, ID *id) return NULL; } -static int rna_KeyBlock_normals_vert_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_vert_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totvert : 0; length[1] = 3; @@ -211,9 +220,10 @@ static void rna_KeyBlock_normals_vert_calc(ID *id, BKE_keyblock_mesh_calc_normals(data, me, (float(*)[3])(*normals), NULL, NULL); } -static int rna_KeyBlock_normals_poly_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_poly_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totpoly : 0; length[1] = 3; @@ -240,9 +250,10 @@ static void rna_KeyBlock_normals_poly_calc(ID *id, BKE_keyblock_mesh_calc_normals(data, me, NULL, (float(*)[3])(*normals), NULL); } -static int rna_KeyBlock_normals_loop_len(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_KeyBlock_normals_loop_len(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); + const Mesh *me = rna_KeyBlock_normals_get_mesh(ptr, NULL); length[0] = me ? me->totloop : 0; length[1] = 3; @@ -656,10 +667,10 @@ int rna_ShapeKey_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) return false; } -static char *rna_ShapeKey_path(PointerRNA *ptr) +static char *rna_ShapeKey_path(const PointerRNA *ptr) { - KeyBlock *kb = (KeyBlock *)ptr->data; - ID *id = ptr->owner_id; + const KeyBlock *kb = (KeyBlock *)ptr->data; + const ID *id = ptr->owner_id; char name_esc[sizeof(kb->name) * 2]; BLI_str_escape(name_esc, kb->name, sizeof(name_esc)); @@ -750,7 +761,7 @@ static int rna_ShapeKeyPoint_get_index(Key *key, KeyBlock *kb, float *point) return (int)(pt - start) / key->elemsize; } -static char *rna_ShapeKeyPoint_path(PointerRNA *ptr) +static char *rna_ShapeKeyPoint_path(const PointerRNA *ptr) { ID *id = ptr->owner_id; Key *key = rna_ShapeKey_find_key(ptr->owner_id); @@ -786,14 +797,6 @@ static char *rna_ShapeKeyPoint_path(PointerRNA *ptr) #else -const EnumPropertyItem rna_enum_keyblock_type_items[] = { - {KEY_LINEAR, "KEY_LINEAR", 0, "Linear", ""}, - {KEY_CARDINAL, "KEY_CARDINAL", 0, "Cardinal", ""}, - {KEY_CATMULL_ROM, "KEY_CATMULL_ROM", 0, "Catmull-Rom", ""}, - {KEY_BSPLINE, "KEY_BSPLINE", 0, "BSpline", ""}, - {0, NULL, 0, NULL, NULL}, -}; - static const float tilt_limit = DEG2RADF(21600.0f); static void rna_def_keydata(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_lattice.c b/source/blender/makesrna/intern/rna_lattice.c index 8fb36c035b7..f94804cb609 100644 --- a/source/blender/makesrna/intern/rna_lattice.c +++ b/source/blender/makesrna/intern/rna_lattice.c @@ -209,11 +209,11 @@ static void rna_Lattice_vg_name_set(PointerRNA *ptr, const char *value) } /* annoying, but is a consequence of RNA structures... */ -static char *rna_LatticePoint_path(PointerRNA *ptr) +static char *rna_LatticePoint_path(const PointerRNA *ptr) { - Lattice *lt = (Lattice *)ptr->owner_id; - void *point = ptr->data; - BPoint *points = NULL; + const Lattice *lt = (Lattice *)ptr->owner_id; + const void *point = ptr->data; + const BPoint *points = NULL; if (lt->editlatt && lt->editlatt->latt->def) { points = lt->editlatt->latt->def; diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 42414a9931b..8c9c66bffcf 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -98,7 +98,7 @@ static void rna_LayerObjects_active_object_set(PointerRNA *ptr, } } -size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer, +size_t rna_ViewLayer_path_buffer_get(const ViewLayer *view_layer, char *r_rna_path, const size_t rna_path_buffer_size) { @@ -108,9 +108,9 @@ size_t rna_ViewLayer_path_buffer_get(ViewLayer *view_layer, return BLI_snprintf_rlen(r_rna_path, rna_path_buffer_size, "view_layers[\"%s\"]", name_esc); } -static char *rna_ViewLayer_path(PointerRNA *ptr) +static char *rna_ViewLayer_path(const PointerRNA *ptr) { - ViewLayer *view_layer = (ViewLayer *)ptr->data; + const ViewLayer *view_layer = (ViewLayer *)ptr->data; char rna_path[sizeof(view_layer->name) * 3]; rna_ViewLayer_path_buffer_get(view_layer, rna_path, sizeof(rna_path)); diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index 2a8a3a76c6d..c18ee461fae 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -239,33 +239,33 @@ static StructRNA *rna_LineStyle_geometry_modifier_refine(struct PointerRNA *ptr) } } -static char *rna_LineStyle_color_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_color_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("color_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_alpha_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_alpha_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("alpha_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_thickness_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_thickness_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("thickness_modifiers[\"%s\"]", name_esc); } -static char *rna_LineStyle_geometry_modifier_path(PointerRNA *ptr) +static char *rna_LineStyle_geometry_modifier_path(const PointerRNA *ptr) { - LineStyleModifier *m = (LineStyleModifier *)ptr->data; + const LineStyleModifier *m = (LineStyleModifier *)ptr->data; char name_esc[sizeof(m->name) * 2]; BLI_str_escape(name_esc, m->name, sizeof(name_esc)); return BLI_sprintfN("geometry_modifiers[\"%s\"]", name_esc); diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index 2247a16a7a0..3dac148f7d4 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -165,9 +165,9 @@ static void rna_Mask_layer_active_index_range( *softmax = *max; } -static char *rna_MaskLayer_path(PointerRNA *ptr) +static char *rna_MaskLayer_path(const PointerRNA *ptr) { - MaskLayer *masklay = (MaskLayer *)ptr->data; + const MaskLayer *masklay = (MaskLayer *)ptr->data; char name_esc[sizeof(masklay->name) * 2]; BLI_str_escape(name_esc, masklay->name, sizeof(name_esc)); return BLI_sprintfN("layers[\"%s\"]", name_esc); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 15e7e12bbf8..4fb7495bac8 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -25,24 +25,24 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = { {MA_RAMP_BLEND, "MIX", 0, "Mix", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MA_RAMP_DARK, "DARKEN", 0, "Darken", ""}, {MA_RAMP_MULT, "MULTIPLY", 0, "Multiply", ""}, {MA_RAMP_BURN, "BURN", 0, "Color Burn", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MA_RAMP_LIGHT, "LIGHTEN", 0, "Lighten", ""}, {MA_RAMP_SCREEN, "SCREEN", 0, "Screen", ""}, {MA_RAMP_DODGE, "DODGE", 0, "Color Dodge", ""}, {MA_RAMP_ADD, "ADD", 0, "Add", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MA_RAMP_OVERLAY, "OVERLAY", 0, "Overlay", ""}, {MA_RAMP_SOFT, "SOFT_LIGHT", 0, "Soft Light", ""}, {MA_RAMP_LINEAR, "LINEAR_LIGHT", 0, "Linear Light", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MA_RAMP_DIFF, "DIFFERENCE", 0, "Difference", ""}, {MA_RAMP_SUB, "SUBTRACT", 0, "Subtract", ""}, {MA_RAMP_DIV, "DIVIDE", 0, "Divide", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MA_RAMP_HUE, "HUE", 0, "Hue", ""}, {MA_RAMP_SAT, "SATURATION", 0, "Saturation", ""}, {MA_RAMP_COLOR, "COLOR", 0, "Color", ""}, @@ -362,7 +362,7 @@ static void rna_gpcolordata_uv_update(Main *bmain, Scene *scene, PointerRNA *ptr rna_MaterialGpencil_update(bmain, scene, ptr); } -static char *rna_GpencilColorData_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpencilColorData_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grease_pencil"); } diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index ec0a182e338..da4be3cd0da 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -71,7 +71,7 @@ static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = { /** \name Generic Helpers * \{ */ -static Mesh *rna_mesh(PointerRNA *ptr) +static Mesh *rna_mesh(const PointerRNA *ptr) { Mesh *me = (Mesh *)ptr->owner_id; return me; @@ -102,7 +102,7 @@ static CustomData *rna_mesh_fdata_helper(Mesh *me) return (me->edit_mesh) ? NULL : &me->fdata; } -static CustomData *rna_mesh_vdata(PointerRNA *ptr) +static CustomData *rna_mesh_vdata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_vdata_helper(me); @@ -114,13 +114,13 @@ static CustomData *rna_mesh_edata(PointerRNA *ptr) return rna_mesh_edata_helper(me); } # endif -static CustomData *rna_mesh_pdata(PointerRNA *ptr) +static CustomData *rna_mesh_pdata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_pdata_helper(me); } -static CustomData *rna_mesh_ldata(PointerRNA *ptr) +static CustomData *rna_mesh_ldata(const PointerRNA *ptr) { Mesh *me = rna_mesh(ptr); return rna_mesh_ldata_helper(me); @@ -560,7 +560,7 @@ static void rna_MeshVertex_undeformed_co_get(PointerRNA *ptr, float values[3]) { Mesh *me = rna_mesh(ptr); MVert *mvert = (MVert *)ptr->data; - float(*orco)[3] = CustomData_get_layer(&me->vdata, CD_ORCO); + const float(*orco)[3] = CustomData_get_layer(&me->vdata, CD_ORCO); if (orco) { /* orco is normalized to 0..1, we do inverse to match mvert->co */ @@ -626,9 +626,10 @@ static void rna_CustomDataLayer_clone_set(PointerRNA *ptr, CustomData *data, int static bool rna_MEdge_freestyle_edge_mark_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MEdge *medge = (MEdge *)ptr->data; - FreestyleEdge *fed = CustomData_get(&me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE); + const Mesh *me = rna_mesh(ptr); + const MEdge *medge = (MEdge *)ptr->data; + const FreestyleEdge *fed = CustomData_get( + &me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE); return fed && (fed->flag & FREESTYLE_EDGE_MARK) != 0; } @@ -652,9 +653,10 @@ static void rna_MEdge_freestyle_edge_mark_set(PointerRNA *ptr, bool value) static bool rna_MPoly_freestyle_face_mark_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MPoly *mpoly = (MPoly *)ptr->data; - FreestyleFace *ffa = CustomData_get(&me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE); + const Mesh *me = rna_mesh(ptr); + const MPoly *mpoly = (MPoly *)ptr->data; + const FreestyleFace *ffa = CustomData_get( + &me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE); return ffa && (ffa->flag & FREESTYLE_FACE_MARK) != 0; } @@ -687,9 +689,9 @@ DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(uv_layer, ldata, CD_MLOOPUV, rende /* MeshUVLoopLayer */ -static char *rna_MeshUVLoopLayer_path(PointerRNA *ptr) +static char *rna_MeshUVLoopLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("uv_layers[\"%s\"]", name_esc); @@ -930,16 +932,16 @@ static int rna_Mesh_polygon_string_layers_length(PointerRNA *ptr) /* Skin vertices */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(skin_vertice, vdata, CD_MVERT_SKIN) -static char *rna_MeshSkinVertexLayer_path(PointerRNA *ptr) +static char *rna_MeshSkinVertexLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("skin_vertices[\"%s\"]", name_esc); } -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type); -static char *rna_MeshSkinVertex_path(PointerRNA *ptr) +static char *rna_VertCustomData_data_path(const PointerRNA *ptr, const char *collection, int type); +static char *rna_MeshSkinVertex_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "skin_vertices", CD_MVERT_SKIN); } @@ -962,8 +964,7 @@ static int rna_MeshSkinVertexLayer_data_length(PointerRNA *ptr) /* Vertex creases */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_crease, vdata, CD_CREASE) -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type); -static char *rna_MeshVertexCreaseLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexCreaseLayer_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_creases", CD_CREASE); } @@ -986,15 +987,15 @@ static int rna_MeshVertexCreaseLayer_data_length(PointerRNA *ptr) /* Paint mask */ DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_paint_mask, vdata, CD_PAINT_MASK) -static char *rna_MeshPaintMaskLayer_path(PointerRNA *ptr) +static char *rna_MeshPaintMaskLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_paint_masks[\"%s\"]", name_esc); } -static char *rna_MeshPaintMask_path(PointerRNA *ptr) +static char *rna_MeshPaintMask_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_paint_masks", CD_PAINT_MASK); } @@ -1021,9 +1022,9 @@ DEFINE_CUSTOMDATA_LAYER_COLLECTION(face_map, pdata, CD_FACEMAP) DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM( face_map, pdata, CD_FACEMAP, active, MeshFaceMapLayer) -static char *rna_MeshFaceMapLayer_path(PointerRNA *ptr) +static char *rna_MeshFaceMapLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("face_maps[\"%s\"]", name_esc); @@ -1088,9 +1089,10 @@ static void rna_Mesh_face_map_remove(struct Mesh *me, /* End face maps */ /* poly.vertices - this is faked loop access for convenience */ -static int rna_MeshPoly_vertices_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_MeshPoly_vertices_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - MPoly *mp = (MPoly *)ptr->data; + const MPoly *mp = (MPoly *)ptr->data; /* NOTE: raw access uses dummy item, this _could_ crash, * watch out for this, mface uses it but it can't work here. */ return (length[0] = mp->totloop); @@ -1180,11 +1182,11 @@ static int rna_MeshLoop_index_get(PointerRNA *ptr) /* path construction */ -static char *rna_VertexGroupElement_path(PointerRNA *ptr) +static char *rna_VertexGroupElement_path(const PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); /* XXX not always! */ - MDeformWeight *dw = (MDeformWeight *)ptr->data; - MDeformVert *dvert; + const Mesh *me = rna_mesh(ptr); /* XXX not always! */ + const MDeformWeight *dw = (MDeformWeight *)ptr->data; + const MDeformVert *dvert; int a, b; for (a = 0, dvert = me->dvert; a < me->totvert; a++, dvert++) { @@ -1198,37 +1200,37 @@ static char *rna_VertexGroupElement_path(PointerRNA *ptr) return NULL; } -static char *rna_MeshPolygon_path(PointerRNA *ptr) +static char *rna_MeshPolygon_path(const PointerRNA *ptr) { return BLI_sprintfN("polygons[%d]", (int)((MPoly *)ptr->data - rna_mesh(ptr)->mpoly)); } -static char *rna_MeshLoopTriangle_path(PointerRNA *ptr) +static char *rna_MeshLoopTriangle_path(const PointerRNA *ptr) { return BLI_sprintfN("loop_triangles[%d]", (int)((MLoopTri *)ptr->data - rna_mesh(ptr)->runtime.looptris.array)); } -static char *rna_MeshEdge_path(PointerRNA *ptr) +static char *rna_MeshEdge_path(const PointerRNA *ptr) { return BLI_sprintfN("edges[%d]", (int)((MEdge *)ptr->data - rna_mesh(ptr)->medge)); } -static char *rna_MeshLoop_path(PointerRNA *ptr) +static char *rna_MeshLoop_path(const PointerRNA *ptr) { return BLI_sprintfN("loops[%d]", (int)((MLoop *)ptr->data - rna_mesh(ptr)->mloop)); } -static char *rna_MeshVertex_path(PointerRNA *ptr) +static char *rna_MeshVertex_path(const PointerRNA *ptr) { return BLI_sprintfN("vertices[%d]", (int)((MVert *)ptr->data - rna_mesh(ptr)->mvert)); } -static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_VertCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *vdata = rna_mesh_vdata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *vdata = rna_mesh_vdata(ptr); int a, b, totvert = (me->edit_mesh) ? 0 : me->totvert; for (cdl = vdata->layers, a = 0; a < vdata->totlayer; cdl++, a++) { @@ -1245,11 +1247,11 @@ static char *rna_VertCustomData_data_path(PointerRNA *ptr, const char *collectio return NULL; } -static char *rna_PolyCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_PolyCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *pdata = rna_mesh_pdata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *pdata = rna_mesh_pdata(ptr); int a, b, totpoly = (me->edit_mesh) ? 0 : me->totpoly; for (cdl = pdata->layers, a = 0; a < pdata->totlayer; cdl++, a++) { @@ -1266,11 +1268,11 @@ static char *rna_PolyCustomData_data_path(PointerRNA *ptr, const char *collectio return NULL; } -static char *rna_LoopCustomData_data_path(PointerRNA *ptr, const char *collection, int type) +static char *rna_LoopCustomData_data_path(const PointerRNA *ptr, const char *collection, int type) { - CustomDataLayer *cdl; - Mesh *me = rna_mesh(ptr); - CustomData *ldata = rna_mesh_ldata(ptr); + const CustomDataLayer *cdl; + const Mesh *me = rna_mesh(ptr); + const CustomData *ldata = rna_mesh_ldata(ptr); int a, b, totloop = (me->edit_mesh) ? 0 : me->totloop; for (cdl = ldata->layers, a = 0; a < ldata->totlayer; cdl++, a++) { @@ -1313,58 +1315,58 @@ static int rna_Mesh_poly_normals_length(PointerRNA *ptr) return mesh->totpoly; } -static char *rna_MeshUVLoop_path(PointerRNA *ptr) +static char *rna_MeshUVLoop_path(const PointerRNA *ptr) { return rna_LoopCustomData_data_path(ptr, "uv_layers", CD_MLOOPUV); } -static char *rna_MeshLoopColorLayer_path(PointerRNA *ptr) +static char *rna_MeshLoopColorLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_colors[\"%s\"]", name_esc); } -static char *rna_MeshColor_path(PointerRNA *ptr) +static char *rna_MeshColor_path(const PointerRNA *ptr) { return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_PROP_BYTE_COLOR); } -static char *rna_MeshVertColorLayer_path(PointerRNA *ptr) +static char *rna_MeshVertColorLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("sculpt_vertex_colors[\"%s\"]", name_esc); } -static char *rna_MeshVertColor_path(PointerRNA *ptr) +static char *rna_MeshVertColor_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "sculpt_vertex_colors", CD_PROP_COLOR); } /**** Float Property Layer API ****/ -static char *rna_MeshVertexFloatPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexFloatPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_float_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonFloatPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonFloatPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_float_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexFloatProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexFloatProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_float", CD_PROP_FLOAT); } -static char *rna_MeshPolygonFloatProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonFloatProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_float", CD_PROP_FLOAT); } @@ -1396,26 +1398,26 @@ static int rna_MeshPolygonFloatPropertyLayer_data_length(PointerRNA *ptr) } /**** Int Property Layer API ****/ -static char *rna_MeshVertexIntPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexIntPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_int_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonIntPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonIntPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_int_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexIntProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexIntProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_int", CD_PROP_INT32); } -static char *rna_MeshPolygonIntProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonIntProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_int", CD_PROP_INT32); } @@ -1447,26 +1449,26 @@ static int rna_MeshPolygonIntPropertyLayer_data_length(PointerRNA *ptr) } /**** String Property Layer API ****/ -static char *rna_MeshVertexStringPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshVertexStringPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("vertex_string_layers[\"%s\"]", name_esc); } -static char *rna_MeshPolygonStringPropertyLayer_path(PointerRNA *ptr) +static char *rna_MeshPolygonStringPropertyLayer_path(const PointerRNA *ptr) { - CustomDataLayer *cdl = ptr->data; + const CustomDataLayer *cdl = ptr->data; char name_esc[sizeof(cdl->name) * 2]; BLI_str_escape(name_esc, cdl->name, sizeof(name_esc)); return BLI_sprintfN("polygon_string_layers[\"%s\"]", name_esc); } -static char *rna_MeshVertexStringProperty_path(PointerRNA *ptr) +static char *rna_MeshVertexStringProperty_path(const PointerRNA *ptr) { return rna_VertCustomData_data_path(ptr, "vertex_layers_string", CD_PROP_STRING); } -static char *rna_MeshPolygonStringProperty_path(PointerRNA *ptr) +static char *rna_MeshPolygonStringProperty_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "polygon_layers_string", CD_PROP_STRING); } @@ -1517,7 +1519,7 @@ void rna_MeshStringProperty_s_set(PointerRNA *ptr, const char *value) BLI_strncpy(ms->s, value, sizeof(ms->s)); } -static char *rna_MeshFaceMap_path(PointerRNA *ptr) +static char *rna_MeshFaceMap_path(const PointerRNA *ptr) { return rna_PolyCustomData_data_path(ptr, "face_maps", CD_FACEMAP); } @@ -1636,7 +1638,7 @@ static PointerRNA rna_Mesh_uv_layers_new(struct Mesh *me, PointerRNA ptr; CustomData *ldata; CustomDataLayer *cdl = NULL; - int index = ED_mesh_uv_texture_add(me, name, false, do_init, reports); + int index = ED_mesh_uv_add(me, name, false, do_init, reports); if (index != -1) { ldata = rna_mesh_ldata_helper(me); @@ -1649,7 +1651,7 @@ static PointerRNA rna_Mesh_uv_layers_new(struct Mesh *me, static void rna_Mesh_uv_layers_remove(struct Mesh *me, ReportList *reports, CustomDataLayer *layer) { - if (ED_mesh_uv_texture_remove_named(me, layer->name) == false) { + if (ED_mesh_uv_remove_named(me, layer->name) == false) { BKE_reportf(reports, RPT_ERROR, "Texture layer '%s' not found", layer->name); } } diff --git a/source/blender/makesrna/intern/rna_meta.c b/source/blender/makesrna/intern/rna_meta.c index f160388c5d2..e6cf743e167 100644 --- a/source/blender/makesrna/intern/rna_meta.c +++ b/source/blender/makesrna/intern/rna_meta.c @@ -156,10 +156,10 @@ static bool rna_Meta_is_editmode_get(PointerRNA *ptr) return (mb->editelems != NULL); } -static char *rna_MetaElement_path(PointerRNA *ptr) +static char *rna_MetaElement_path(const PointerRNA *ptr) { - MetaBall *mb = (MetaBall *)ptr->owner_id; - MetaElem *ml = ptr->data; + const MetaBall *mb = (MetaBall *)ptr->owner_id; + const MetaElem *ml = ptr->data; int index = -1; if (mb->editelems) { diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 456f774648a..4c410782326 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -47,7 +47,7 @@ #include "MOD_nodes.h" const EnumPropertyItem rna_enum_object_modifier_type_items[] = { - {0, "", 0, N_("Modify"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Modify"), NULL), {eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, @@ -99,7 +99,8 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_VERTEX_WEIGHT, "Vertex Weight Proximity", "Set the vertex group weights based on the distance to another target object"}, - {0, "", 0, N_("Generate"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Generate"), NULL), {eModifierType_Array, "ARRAY", ICON_MOD_ARRAY, @@ -130,7 +131,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_EDGESPLIT, "Edge Split", "Split away joined faces at the edges"}, - {eModifierType_Nodes, "NODES", ICON_NODETREE, "Geometry Nodes", ""}, + {eModifierType_Nodes, "NODES", ICON_GEOMETRY_NODES, "Geometry Nodes", ""}, {eModifierType_Mask, "MASK", ICON_MOD_MASK, @@ -193,7 +194,8 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_WIREFRAME, "Wireframe", "Convert faces into thickened edges"}, - {0, "", 0, N_("Deform"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Deform"), NULL), {eModifierType_Armature, "ARMATURE", ICON_MOD_ARMATURE, @@ -272,7 +274,8 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_VOLUME_DATA, "Volume Displace", "Deform volume based on noise or other vector fields"}, /* TODO: Use correct icon. */ - {0, "", 0, N_("Physics"), ""}, + + RNA_ENUM_ITEM_HEADING(N_("Physics"), NULL), {eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""}, {eModifierType_Collision, "COLLISION", ICON_MOD_PHYSICS, "Collision", ""}, {eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""}, @@ -722,9 +725,9 @@ static void rna_Modifier_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name); } -static char *rna_Modifier_path(PointerRNA *ptr) +static char *rna_Modifier_path(const PointerRNA *ptr) { - ModifierData *md = ptr->data; + const ModifierData *md = ptr->data; char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); @@ -947,10 +950,10 @@ static void rna_HookModifier_subtarget_set(PointerRNA *ptr, const char *value) BKE_object_modifier_hook_reset(owner, hmd); } -static int rna_HookModifier_vertex_indices_get_length(PointerRNA *ptr, +static int rna_HookModifier_vertex_indices_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { - HookModifierData *hmd = ptr->data; + const HookModifierData *hmd = ptr->data; int indexar_num = hmd->indexar ? hmd->indexar_num : 0; return (length[0] = indexar_num); } @@ -6991,7 +6994,7 @@ static void rna_def_modifier_nodes(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Nodes Modifier", ""); RNA_def_struct_sdna(srna, "NodesModifierData"); RNA_def_struct_idprops_func(srna, "rna_NodesModifier_properties"); - RNA_def_struct_ui_icon(srna, ICON_NODETREE); + RNA_def_struct_ui_icon(srna, ICON_GEOMETRY_NODES); RNA_define_lib_overridable(true); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index f8a35246581..978a94ca7b0 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -23,6 +23,49 @@ #include "WM_api.h" #include "WM_types.h" +/* enum defines exported for rna_animation.c */ +const EnumPropertyItem rna_enum_nla_mode_blend_items[] = { + {NLASTRIP_MODE_REPLACE, + "REPLACE", + 0, + "Replace", + "The strip values replace the accumulated results by amount specified by influence"}, + {NLASTRIP_MODE_COMBINE, + "COMBINE", + 0, + "Combine", + "The strip values are combined with accumulated results by appropriately using addition, " + "multiplication, or quaternion math, based on channel type"}, + RNA_ENUM_ITEM_SEPR, + {NLASTRIP_MODE_ADD, + "ADD", + 0, + "Add", + "Weighted result of strip is added to the accumulated results"}, + {NLASTRIP_MODE_SUBTRACT, + "SUBTRACT", + 0, + "Subtract", + "Weighted result of strip is removed from the accumulated results"}, + {NLASTRIP_MODE_MULTIPLY, + "MULTIPLY", + 0, + "Multiply", + "Weighted result of strip is multiplied with the accumulated results"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_nla_mode_extend_items[] = { + {NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents"}, + {NLASTRIP_EXTEND_HOLD, + "HOLD", + 0, + "Hold", + "Hold the first frame if no previous strips in track, and always hold last frame"}, + {NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include <math.h> @@ -57,7 +100,7 @@ static void rna_NlaStrip_name_set(PointerRNA *ptr, const char *value) } } -static char *rna_NlaStrip_path(PointerRNA *ptr) +static char *rna_NlaStrip_path(const PointerRNA *ptr) { NlaStrip *strip = (NlaStrip *)ptr->data; AnimData *adt = BKE_animdata_from_id(ptr->owner_id); @@ -494,49 +537,6 @@ static void rna_NlaTrack_solo_set(PointerRNA *ptr, bool value) #else -/* enum defines exported for rna_animation.c */ -const EnumPropertyItem rna_enum_nla_mode_blend_items[] = { - {NLASTRIP_MODE_REPLACE, - "REPLACE", - 0, - "Replace", - "The strip values replace the accumulated results by amount specified by influence"}, - {NLASTRIP_MODE_COMBINE, - "COMBINE", - 0, - "Combine", - "The strip values are combined with accumulated results by appropriately using addition, " - "multiplication, or quaternion math, based on channel type"}, - {0, "", 0, NULL, NULL}, - {NLASTRIP_MODE_ADD, - "ADD", - 0, - "Add", - "Weighted result of strip is added to the accumulated results"}, - {NLASTRIP_MODE_SUBTRACT, - "SUBTRACT", - 0, - "Subtract", - "Weighted result of strip is removed from the accumulated results"}, - {NLASTRIP_MODE_MULTIPLY, - "MULTIPLY", - 0, - "Multiply", - "Weighted result of strip is multiplied with the accumulated results"}, - {0, NULL, 0, NULL, NULL}, -}; - -const EnumPropertyItem rna_enum_nla_mode_extend_items[] = { - {NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents"}, - {NLASTRIP_EXTEND_HOLD, - "HOLD", - 0, - "Hold", - "Hold the first frame if no previous strips in track, and always hold last frame"}, - {NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame"}, - {0, NULL, 0, NULL, NULL}, -}; - static void rna_def_strip_fcurves(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index eaf2142495e..841c250df4c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -164,20 +164,20 @@ static const EnumPropertyItem rna_enum_vector_rotate_type_items[] = { }; const EnumPropertyItem rna_enum_node_math_items[] = { - {0, "", 0, N_("Functions"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Functions"), NULL), {NODE_MATH_ADD, "ADD", 0, "Add", "A + B"}, {NODE_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"}, {NODE_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "A * B"}, {NODE_MATH_DIVIDE, "DIVIDE", 0, "Divide", "A / B"}, {NODE_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_MATH_POWER, "POWER", 0, "Power", "A power B"}, {NODE_MATH_LOGARITHM, "LOGARITHM", 0, "Logarithm", "Logarithm A base B"}, {NODE_MATH_SQRT, "SQRT", 0, "Square Root", "Square root of A"}, {NODE_MATH_INV_SQRT, "INVERSE_SQRT", 0, "Inverse Square Root", "1 / Square root of A"}, {NODE_MATH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Magnitude of A"}, {NODE_MATH_EXPONENT, "EXPONENT", 0, "Exponent", "exp(A)"}, - {0, "", 0, N_("Comparison"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Comparison"), NULL), {NODE_MATH_MINIMUM, "MINIMUM", 0, "Minimum", "The minimum from A and B"}, {NODE_MATH_MAXIMUM, "MAXIMUM", 0, "Maximum", "The maximum from A and B"}, {NODE_MATH_LESS_THAN, "LESS_THAN", 0, "Less Than", "1 if A < B else 0"}, @@ -194,7 +194,7 @@ const EnumPropertyItem rna_enum_node_math_items[] = { 0, "Smooth Maximum", "The maximum from A and B with smoothing C"}, - {0, "", 0, N_("Rounding"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Rounding"), NULL), {NODE_MATH_ROUND, "ROUND", 0, @@ -203,7 +203,7 @@ const EnumPropertyItem rna_enum_node_math_items[] = { {NODE_MATH_FLOOR, "FLOOR", 0, "Floor", "The largest integer smaller than or equal A"}, {NODE_MATH_CEIL, "CEIL", 0, "Ceil", "The smallest integer greater than or equal A"}, {NODE_MATH_TRUNC, "TRUNC", 0, "Truncate", "The integer part of A, removing fractional digits"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_MATH_FRACTION, "FRACT", 0, "Fraction", "The fraction part of A"}, {NODE_MATH_MODULO, "MODULO", 0, "Modulo", "Modulo using fmod(A,B)"}, {NODE_MATH_WRAP, "WRAP", 0, "Wrap", "Wrap value to range, wrap(A,B)"}, @@ -213,20 +213,20 @@ const EnumPropertyItem rna_enum_node_math_items[] = { 0, "Ping-Pong", "Wraps a value and reverses every other cycle (A,B)"}, - {0, "", 0, N_("Trigonometric"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Trigonometric"), NULL), {NODE_MATH_SINE, "SINE", 0, "Sine", "sin(A)"}, {NODE_MATH_COSINE, "COSINE", 0, "Cosine", "cos(A)"}, {NODE_MATH_TANGENT, "TANGENT", 0, "Tangent", "tan(A)"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_MATH_ARCSINE, "ARCSINE", 0, "Arcsine", "arcsin(A)"}, {NODE_MATH_ARCCOSINE, "ARCCOSINE", 0, "Arccosine", "arccos(A)"}, {NODE_MATH_ARCTANGENT, "ARCTANGENT", 0, "Arctangent", "arctan(A)"}, {NODE_MATH_ARCTAN2, "ARCTAN2", 0, "Arctan2", "The signed angle arctan(A / B)"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_MATH_SINH, "SINH", 0, "Hyperbolic Sine", "sinh(A)"}, {NODE_MATH_COSH, "COSH", 0, "Hyperbolic Cosine", "cosh(A)"}, {NODE_MATH_TANH, "TANH", 0, "Hyperbolic Tangent", "tanh(A)"}, - {0, "", 0, N_("Conversion"), ""}, + RNA_ENUM_ITEM_HEADING(N_("Conversion"), NULL), {NODE_MATH_RADIANS, "RADIANS", 0, "To Radians", "Convert from degrees to radians"}, {NODE_MATH_DEGREES, "DEGREES", 0, "To Degrees", "Convert from radians to degrees"}, {0, NULL, 0, NULL, NULL}, @@ -238,7 +238,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { {NODE_VECTOR_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "Entry-wise multiply"}, {NODE_VECTOR_MATH_DIVIDE, "DIVIDE", 0, "Divide", "Entry-wise divide"}, {NODE_VECTOR_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_VECTOR_MATH_CROSS_PRODUCT, "CROSS_PRODUCT", 0, "Cross Product", "A cross B"}, {NODE_VECTOR_MATH_PROJECT, "PROJECT", 0, "Project", "Project A onto B"}, {NODE_VECTOR_MATH_REFLECT, @@ -259,12 +259,12 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { "Orients a vector A to point away from a surface B as defined by its normal C. " "Returns (dot(B, C) < 0) ? A : -A"}, {NODE_VECTOR_MATH_DOT_PRODUCT, "DOT_PRODUCT", 0, "Dot Product", "A dot B"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_VECTOR_MATH_DISTANCE, "DISTANCE", 0, "Distance", "Distance between A and B"}, {NODE_VECTOR_MATH_LENGTH, "LENGTH", 0, "Length", "Length of A"}, {NODE_VECTOR_MATH_SCALE, "SCALE", 0, "Scale", "A multiplied by Scale"}, {NODE_VECTOR_MATH_NORMALIZE, "NORMALIZE", 0, "Normalize", "Normalize A"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_VECTOR_MATH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Entry-wise absolute"}, {NODE_VECTOR_MATH_MINIMUM, "MINIMUM", 0, "Minimum", "Entry-wise minimum"}, {NODE_VECTOR_MATH_MAXIMUM, "MAXIMUM", 0, "Maximum", "Entry-wise maximum"}, @@ -278,7 +278,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = { 0, "Snap", "Round A to the largest integer multiple of B less than or equal A"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_VECTOR_MATH_SINE, "SINE", 0, "Sine", "Entry-wise sin(A)"}, {NODE_VECTOR_MATH_COSINE, "COSINE", 0, "Cosine", "Entry-wise cos(A)"}, {NODE_VECTOR_MATH_TANGENT, "TANGENT", 0, "Tangent", "Entry-wise tan(A)"}, @@ -289,7 +289,7 @@ const EnumPropertyItem rna_enum_node_boolean_math_items[] = { {NODE_BOOLEAN_MATH_AND, "AND", 0, "And", "True when both inputs are true"}, {NODE_BOOLEAN_MATH_OR, "OR", 0, "Or", "True when at least one input is true"}, {NODE_BOOLEAN_MATH_NOT, "NOT", 0, "Not", "Opposite of the input"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_BOOLEAN_MATH_NAND, "NAND", 0, "Not And", "True when at least one input is false"}, {NODE_BOOLEAN_MATH_NOR, "NOR", 0, "Nor", "True when both inputs are false"}, {NODE_BOOLEAN_MATH_XNOR, @@ -302,7 +302,7 @@ const EnumPropertyItem rna_enum_node_boolean_math_items[] = { 0, "Not Equal", "True when both inputs are different (exclusive or)"}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NODE_BOOLEAN_MATH_IMPLY, "IMPLY", 0, @@ -443,9 +443,9 @@ const EnumPropertyItem rna_enum_node_clamp_items[] = { static const EnumPropertyItem rna_enum_node_tex_dimensions_items[] = { {1, "1D", 0, "1D", "Use the scalar value W as input"}, - {2, "2D", 0, "2D", "Use the 2D vector (x, y) as input. The z component is ignored"}, - {3, "3D", 0, "3D", "Use the 3D vector (x, y, z) as input"}, - {4, "4D", 0, "4D", "Use the 4D vector (x, y, z, w) as input"}, + {2, "2D", 0, "2D", "Use the 2D vector (X, Y) as input. The Z component is ignored"}, + {3, "3D", 0, "3D", "Use the 3D vector (X, Y, Z) as input"}, + {4, "4D", 0, "4D", "Use the 4D vector (X, Y, Z, W) as input"}, {0, NULL, 0, NULL, NULL}, }; @@ -489,6 +489,13 @@ static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = { {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem rna_node_combsep_color_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static const EnumPropertyItem node_sampler_type_items[] = { {0, "NEAREST", 0, "Nearest", ""}, @@ -1591,16 +1598,16 @@ static StructRNA *rna_Node_refine(struct PointerRNA *ptr) } } -static char *rna_Node_path(PointerRNA *ptr) +static char *rna_Node_path(const PointerRNA *ptr) { - bNode *node = (bNode *)ptr->data; + const bNode *node = (bNode *)ptr->data; char name_esc[sizeof(node->name) * 2]; BLI_str_escape(name_esc, node->name, sizeof(name_esc)); return BLI_sprintfN("nodes[\"%s\"]", name_esc); } -char *rna_Node_ImageUser_path(PointerRNA *ptr) +char *rna_Node_ImageUser_path(const PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node; @@ -2749,7 +2756,7 @@ static StructRNA *rna_NodeSocket_refine(PointerRNA *ptr) } } -static char *rna_NodeSocket_path(PointerRNA *ptr) +static char *rna_NodeSocket_path(const PointerRNA *ptr) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNodeSocket *sock = (bNodeSocket *)ptr->data; @@ -3063,10 +3070,10 @@ static StructRNA *rna_NodeSocketInterface_refine(PointerRNA *ptr) } } -static char *rna_NodeSocketInterface_path(PointerRNA *ptr) +static char *rna_NodeSocketInterface_path(const PointerRNA *ptr) { - bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = (bNodeSocket *)ptr->data; + const bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + const bNodeSocket *sock = (bNodeSocket *)ptr->data; int socketindex; socketindex = BLI_findindex(&ntree->inputs, sock); @@ -4516,9 +4523,9 @@ static const EnumPropertyItem prop_view_layer_items[] = { }; static const EnumPropertyItem prop_tri_channel_items[] = { - {1, "R", 0, "R", ""}, - {2, "G", 0, "G", ""}, - {3, "B", 0, "B", ""}, + {1, "R", 0, "R", "Red"}, + {2, "G", 0, "G", "Green"}, + {3, "B", 0, "B", "Blue"}, {0, NULL, 0, NULL, NULL}, }; @@ -4537,46 +4544,81 @@ static const EnumPropertyItem node_ycc_items[] = { }; static const EnumPropertyItem node_glossy_items[] = { - {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""}, + {SHD_GLOSSY_SHARP, + "SHARP", + 0, + "Sharp", + "Results in perfectly sharp reflections like a mirror. The Roughness value is not used"}, {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, {SHD_GLOSSY_ASHIKHMIN_SHIRLEY, "ASHIKHMIN_SHIRLEY", 0, "Ashikhmin-Shirley", ""}, - {SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", ""}, + {SHD_GLOSSY_MULTI_GGX, + "MULTI_GGX", + 0, + "Multiscatter GGX", + "Slower than GGX but gives a more energy conserving results, which would otherwise be " + "visible as excessive darkening"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem node_anisotropic_items[] = { {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, - {SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", ""}, + {SHD_GLOSSY_MULTI_GGX, + "MULTI_GGX", + 0, + "Multiscatter GGX", + "Slower than GGX but gives a more energy conserving results, which would otherwise be " + "visible as excessive darkening"}, {SHD_GLOSSY_ASHIKHMIN_SHIRLEY, "ASHIKHMIN_SHIRLEY", 0, "Ashikhmin-Shirley", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem node_glass_items[] = { - {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""}, + {SHD_GLOSSY_SHARP, + "SHARP", + 0, + "Sharp", + "Results in perfectly sharp reflections like a mirror. The Roughness value is not used"}, {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, - {SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", ""}, + {SHD_GLOSSY_MULTI_GGX, + "MULTI_GGX", + 0, + "Multiscatter GGX", + "Slower than GGX but gives a more energy conserving results, which would otherwise be " + "visible as excessive darkening"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem node_refraction_items[] = { - {SHD_GLOSSY_SHARP, "SHARP", 0, "Sharp", ""}, + {SHD_GLOSSY_SHARP, + "SHARP", + 0, + "Sharp", + "Results in perfectly sharp reflections like a mirror. The Roughness value is not used"}, {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem node_toon_items[] = { - {SHD_TOON_DIFFUSE, "DIFFUSE", 0, "Diffuse", ""}, - {SHD_TOON_GLOSSY, "GLOSSY", 0, "Glossy", ""}, + {SHD_TOON_DIFFUSE, "DIFFUSE", 0, "Diffuse", "Use diffuse BSDF"}, + {SHD_TOON_GLOSSY, "GLOSSY", 0, "Glossy", "Use glossy BSDF"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem node_hair_items[] = { - {SHD_HAIR_REFLECTION, "Reflection", 0, "Reflection", ""}, - {SHD_HAIR_TRANSMISSION, "Transmission", 0, "Transmission", ""}, + {SHD_HAIR_REFLECTION, + "Reflection", + 0, + "Reflection", + "The light that bounces off the surface of the hair"}, + {SHD_HAIR_TRANSMISSION, + "Transmission", + 0, + "Transmission", + "The light that passes through the hair and exits on the other side"}, {0, NULL, 0, NULL, NULL}, }; @@ -4616,7 +4658,12 @@ static EnumPropertyItem node_ies_mode_items[] = { static const EnumPropertyItem node_principled_distribution_items[] = { {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, - {SHD_GLOSSY_MULTI_GGX, "MULTI_GGX", 0, "Multiscatter GGX", ""}, + {SHD_GLOSSY_MULTI_GGX, + "MULTI_GGX", + 0, + "Multiscatter GGX", + "Slower than GGX but gives a more energy conserving results, which would otherwise be " + "visible as excessive darkening"}, {0, NULL, 0, NULL, NULL}, }; @@ -5050,6 +5097,18 @@ static void def_fn_input_string(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -- Shader Nodes ---------------------------------------------------------- */ static void def_sh_output(StructRNA *srna) @@ -5401,6 +5460,17 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_tex_combsep_color(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_combsep_color_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_image_texture(StructRNA *srna) { static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { @@ -5487,8 +5557,7 @@ static void def_sh_tex_noise(StructRNA *srna) prop = RNA_def_property(srna, "noise_dimensions", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "dimensions"); RNA_def_property_enum_items(prop, rna_enum_node_tex_dimensions_items); - RNA_def_property_ui_text( - prop, "Dimensions", "The dimensions of the space to evaluate the noise in"); + RNA_def_property_ui_text(prop, "Dimensions", "Number of dimensions to output noise for"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); } @@ -5551,11 +5620,28 @@ static void def_sh_tex_magic(StructRNA *srna) static void def_sh_tex_musgrave(StructRNA *srna) { static const EnumPropertyItem prop_musgrave_type[] = { - {SHD_MUSGRAVE_MULTIFRACTAL, "MULTIFRACTAL", 0, "Multifractal", ""}, - {SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, "RIDGED_MULTIFRACTAL", 0, "Ridged Multifractal", ""}, - {SHD_MUSGRAVE_HYBRID_MULTIFRACTAL, "HYBRID_MULTIFRACTAL", 0, "Hybrid Multifractal", ""}, - {SHD_MUSGRAVE_FBM, "FBM", 0, "fBM", ""}, - {SHD_MUSGRAVE_HETERO_TERRAIN, "HETERO_TERRAIN", 0, "Hetero Terrain", ""}, + {SHD_MUSGRAVE_MULTIFRACTAL, + "MULTIFRACTAL", + 0, + "Multifractal", + "More uneven result (varies with location), more similar to a real terrain"}, + {SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, + "RIDGED_MULTIFRACTAL", + 0, + "Ridged Multifractal", + "Create sharp peaks"}, + {SHD_MUSGRAVE_HYBRID_MULTIFRACTAL, + "HYBRID_MULTIFRACTAL", + 0, + "Hybrid Multifractal", + "Create peaks and valleys with different roughness values"}, + {SHD_MUSGRAVE_FBM, "FBM", 0, "fBM", "Produce an unnatural homogeneous and isotropic result"}, + {SHD_MUSGRAVE_HETERO_TERRAIN, + "HETERO_TERRAIN", + 0, + "Hetero Terrain", + "Similar to Hybrid Multifractal creates a heterogeneous terrain, but with the likeness of " + "river channels"}, {0, NULL, 0, NULL, NULL}, }; @@ -5567,13 +5653,13 @@ static void def_sh_tex_musgrave(StructRNA *srna) prop = RNA_def_property(srna, "musgrave_dimensions", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "dimensions"); RNA_def_property_enum_items(prop, rna_enum_node_tex_dimensions_items); - RNA_def_property_ui_text(prop, "Dimensions", ""); + RNA_def_property_ui_text(prop, "Dimensions", "Number of dimensions to output noise for"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "musgrave_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "musgrave_type"); RNA_def_property_enum_items(prop, prop_musgrave_type); - RNA_def_property_ui_text(prop, "Type", ""); + RNA_def_property_ui_text(prop, "Type", "Type of the Musgrave texture"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); } @@ -5622,19 +5708,21 @@ static void def_sh_tex_voronoi(StructRNA *srna) prop = RNA_def_property(srna, "voronoi_dimensions", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "dimensions"); RNA_def_property_enum_items(prop, rna_enum_node_tex_dimensions_items); - RNA_def_property_ui_text(prop, "Dimensions", ""); + RNA_def_property_ui_text(prop, "Dimensions", "Number of dimensions to output noise for"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "distance", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "distance"); RNA_def_property_enum_items(prop, prop_distance_items); - RNA_def_property_ui_text(prop, "Distance Metric", ""); + RNA_def_property_ui_text( + prop, "Distance Metric", "The distance metric used to compute the texture"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "feature", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "feature"); RNA_def_property_enum_items(prop, prop_feature_items); - RNA_def_property_ui_text(prop, "Feature Output", ""); + RNA_def_property_ui_text( + prop, "Feature Output", "The Voronoi feature that the node will compute"); RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); } @@ -5710,8 +5798,7 @@ static void def_sh_tex_white_noise(StructRNA *srna) prop = RNA_def_property(srna, "noise_dimensions", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, rna_enum_node_tex_dimensions_items); - RNA_def_property_ui_text( - prop, "Dimensions", "The dimensions of the space to evaluate the noise in"); + RNA_def_property_ui_text(prop, "Dimensions", "Number of dimensions to output noise for"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); } @@ -5953,7 +6040,7 @@ static void def_glossy(StructRNA *srna) prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_glossy_items); - RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -5964,7 +6051,7 @@ static void def_glass(StructRNA *srna) prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_glass_items); - RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -5975,7 +6062,7 @@ static void def_principled(StructRNA *srna) prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_principled_distribution_items); - RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "subsurface_method", PROP_ENUM, PROP_NONE); @@ -5993,7 +6080,7 @@ static void def_refraction(StructRNA *srna) prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_refraction_items); - RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -6004,7 +6091,7 @@ static void def_anisotropic(StructRNA *srna) prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_anisotropic_items); - RNA_def_property_ui_text(prop, "Distribution", ""); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -6015,7 +6102,7 @@ static void def_toon(StructRNA *srna) prop = RNA_def_property(srna, "component", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_toon_items); - RNA_def_property_ui_text(prop, "Component", ""); + RNA_def_property_ui_text(prop, "Component", "Toon BSDF component to use"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -6037,7 +6124,7 @@ static void def_hair(StructRNA *srna) prop = RNA_def_property(srna, "component", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, node_hair_items); - RNA_def_property_ui_text(prop, "Component", ""); + RNA_def_property_ui_text(prop, "Component", "Hair BSDF component to use"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -6308,6 +6395,25 @@ static void def_sh_output_aov(StructRNA *srna) RNA_def_struct_sdna_from(srna, "bNode", NULL); } +static void def_sh_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_sh_script(StructRNA *srna) { PropertyRNA *prop; @@ -8071,6 +8177,32 @@ static void def_cmp_ycc(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_combsep_color(StructRNA *srna) +{ + static const EnumPropertyItem mode_items[] = { + {CMP_NODE_COMBSEP_COLOR_RGB, "RGB", ICON_NONE, "RGB", "Use RGB color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSV, "HSV", ICON_NONE, "HSV", "Use HSV color processing"}, + {CMP_NODE_COMBSEP_COLOR_HSL, "HSL", ICON_NONE, "HSL", "Use HSL color processing"}, + {CMP_NODE_COMBSEP_COLOR_YCC, "YCC", ICON_NONE, "YCbCr", "Use YCbCr color processing"}, + {CMP_NODE_COMBSEP_COLOR_YUV, "YUV", ICON_NONE, "YUV", "Use YUV color processing"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeCMPCombSepColor", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Mode of color processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "ycc_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, node_ycc_items); + RNA_def_property_ui_text(prop, "Color Space", "Color space used for YCbCrA processing"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_cmp_movieclip(StructRNA *srna) { PropertyRNA *prop; @@ -12294,7 +12426,7 @@ static void rna_def_nodetree(BlenderRNA *brna) {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, - {NTREE_GEOMETRY, "GEOMETRY", ICON_NODETREE, "Geometry", "Geometry nodes"}, + {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 98fc6633f78..56652a35ecb 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -149,7 +149,7 @@ const EnumPropertyItem rna_enum_object_gpencil_type_items[] = { {GP_EMPTY, "EMPTY", ICON_EMPTY_AXIS, "Blank", "Create an empty grease pencil object"}, {GP_STROKE, "STROKE", ICON_STROKE, "Stroke", "Create a simple stroke with basic colors"}, {GP_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Construct a Suzanne grease pencil object"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {GP_LRT_SCENE, "LRT_SCENE", ICON_SCENE_DATA, @@ -256,17 +256,17 @@ const EnumPropertyItem rna_enum_object_type_items[] = { {OB_POINTCLOUD, "POINTCLOUD", ICON_OUTLINER_OB_POINTCLOUD, "Point Cloud", ""}, {OB_VOLUME, "VOLUME", ICON_OUTLINER_OB_VOLUME, "Volume", ""}, {OB_GPENCIL, "GPENCIL", ICON_OUTLINER_OB_GREASEPENCIL, "Grease Pencil", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {OB_ARMATURE, "ARMATURE", ICON_OUTLINER_OB_ARMATURE, "Armature", ""}, {OB_LATTICE, "LATTICE", ICON_OUTLINER_OB_LATTICE, "Lattice", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {OB_EMPTY, "EMPTY", ICON_OUTLINER_OB_EMPTY, "Empty", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {OB_LAMP, "LIGHT", ICON_OUTLINER_OB_LIGHT, "Light", ""}, {OB_LIGHTPROBE, "LIGHT_PROBE", ICON_OUTLINER_OB_LIGHTPROBE, "Light Probe", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {OB_CAMERA, "CAMERA", ICON_OUTLINER_OB_CAMERA, "Camera", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {OB_SPEAKER, "SPEAKER", ICON_OUTLINER_OB_SPEAKER, "Speaker", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -1320,12 +1320,17 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index) return PROP_EDITABLE; } -static int rna_MaterialSlot_index(PointerRNA *ptr) +static int rna_MaterialSlot_index(const PointerRNA *ptr) { /* There is an offset, so that `ptr->data` is not null and unique across IDs. */ return (uintptr_t)ptr->data - (uintptr_t)ptr->owner_id; } +static int rna_MaterialSlot_index_get(PointerRNA *ptr) +{ + return rna_MaterialSlot_index(ptr); +} + static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info)) { Object *ob = (Object *)ptr->owner_id; @@ -1451,7 +1456,7 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr) DEG_relations_tag_update(bmain); } -static char *rna_MaterialSlot_path(PointerRNA *ptr) +static char *rna_MaterialSlot_path(const PointerRNA *ptr) { int index = rna_MaterialSlot_index(ptr); return BLI_sprintfN("material_slots[%d]", index); @@ -1504,7 +1509,7 @@ static PointerRNA rna_Object_display_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ObjectDisplay, ptr->data); } -static char *rna_ObjectDisplay_path(PointerRNA *UNUSED(ptr)) +static char *rna_ObjectDisplay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display"); } @@ -2484,7 +2489,7 @@ static void rna_def_material_slot(BlenderRNA *brna) prop = RNA_def_property(srna, "slot_index", PROP_INT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_int_funcs(prop, "rna_MaterialSlot_index", NULL, NULL); + RNA_def_property_int_funcs(prop, "rna_MaterialSlot_index_get", NULL, NULL); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs( diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f92ea8df459..2ed539aa511 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -130,7 +130,7 @@ static bool rna_Cache_get_valid_owner_ID(PointerRNA *ptr, Object **ob, Scene **s return (*ob != NULL || *scene != NULL); } -static char *rna_PointCache_path(PointerRNA *ptr) +static char *rna_PointCache_path(const PointerRNA *ptr) { ModifierData *md; Object *ob = (Object *)ptr->owner_id; @@ -443,7 +443,7 @@ int rna_Cache_info_length(PointerRNA *ptr) return (int)strlen(cache->info); } -static char *rna_CollisionSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_CollisionSettings_path(const PointerRNA *UNUSED(ptr)) { /* both methods work ok, but return the shorter path */ # if 0 @@ -619,17 +619,17 @@ static void rna_SoftBodySettings_spring_vgroup_set(PointerRNA *ptr, const char * rna_object_vgroup_name_set(ptr, value, sb->namedVG_Spring_K, sizeof(sb->namedVG_Spring_K)); } -static char *rna_SoftBodySettings_path(PointerRNA *ptr) +static char *rna_SoftBodySettings_path(const PointerRNA *ptr) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = (ModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Softbody); + const Object *ob = (Object *)ptr->owner_id; + const ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Softbody); char name_esc[sizeof(md->name) * 2]; BLI_str_escape(name_esc, md->name, sizeof(name_esc)); return BLI_sprintfN("modifiers[\"%s\"].settings", name_esc); } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { ID *id = ptr->owner_id; @@ -731,7 +731,7 @@ static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, Point } } -static char *rna_FieldSettings_path(PointerRNA *ptr) +static char *rna_FieldSettings_path(const PointerRNA *ptr) { PartDeflect *pd = (PartDeflect *)ptr->data; @@ -787,7 +787,7 @@ static void rna_EffectorWeight_dependency_update(Main *bmain, WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); } -static char *rna_EffectorWeight_path(PointerRNA *ptr) +static char *rna_EffectorWeight_path(const PointerRNA *ptr) { EffectorWeights *ew = (EffectorWeights *)ptr->data; /* Check through all possible places the settings can be to find the right one */ @@ -1395,97 +1395,97 @@ static void rna_def_field(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem field_type_items[] = { - {0, "NONE", 0, "None", ""}, + {0, "NONE", ICON_BLANK1, "None", ""}, + {PFIELD_BOID, + "BOID", + ICON_FORCE_BOID, + "Boid", + "Create a force that acts as a boid's predators or target"}, + {PFIELD_CHARGE, + "CHARGE", + ICON_FORCE_CHARGE, + "Charge", + "Spherical forcefield based on the charge of particles, " + "only influences other charge force fields"}, + {PFIELD_GUIDE, + "GUIDE", + ICON_FORCE_CURVE, + "Curve Guide", + "Create a force along a curve object"}, + {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, + {PFIELD_FLUIDFLOW, + "FLUID_FLOW", + ICON_FORCE_FLUIDFLOW, + "Fluid Flow", + "Create a force based on fluid simulation velocities"}, {PFIELD_FORCE, "FORCE", ICON_FORCE_FORCE, "Force", "Radial field toward the center of object"}, - {PFIELD_WIND, - "WIND", - ICON_FORCE_WIND, - "Wind", - "Constant force along the force object's local Z axis"}, - {PFIELD_VORTEX, - "VORTEX", - ICON_FORCE_VORTEX, - "Vortex", - "Spiraling force that twists the force object's local Z axis"}, - {PFIELD_MAGNET, - "MAGNET", - ICON_FORCE_MAGNETIC, - "Magnetic", - "Forcefield depends on the speed of the particles"}, {PFIELD_HARMONIC, "HARMONIC", ICON_FORCE_HARMONIC, "Harmonic", "The source of this force field is the zero point of a harmonic oscillator"}, - {PFIELD_CHARGE, - "CHARGE", - ICON_FORCE_CHARGE, - "Charge", - "Spherical forcefield based on the charge of particles, " - "only influences other charge force fields"}, {PFIELD_LENNARDJ, "LENNARDJ", ICON_FORCE_LENNARDJONES, "Lennard-Jones", "Forcefield based on the Lennard-Jones potential"}, + {PFIELD_MAGNET, + "MAGNET", + ICON_FORCE_MAGNETIC, + "Magnetic", + "Forcefield depends on the speed of the particles"}, {PFIELD_TEXTURE, "TEXTURE", ICON_FORCE_TEXTURE, "Texture", "Force field based on a texture"}, - {PFIELD_GUIDE, - "GUIDE", - ICON_FORCE_CURVE, - "Curve Guide", - "Create a force along a curve object"}, - {PFIELD_BOID, - "BOID", - ICON_FORCE_BOID, - "Boid", - "Create a force that acts as a boid's predators or target"}, {PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", "Create turbulence with a noise field"}, - {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", "Create a force that dampens motion"}, - {PFIELD_FLUIDFLOW, - "FLUID_FLOW", - ICON_FORCE_FLUIDFLOW, - "Fluid Flow", - "Create a force based on fluid simulation velocities"}, + {PFIELD_VORTEX, + "VORTEX", + ICON_FORCE_VORTEX, + "Vortex", + "Spiraling force that twists the force object's local Z axis"}, + {PFIELD_WIND, + "WIND", + ICON_FORCE_WIND, + "Wind", + "Constant force along the force object's local Z axis"}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem falloff_items[] = { + {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {PFIELD_FALL_SPHERE, "SPHERE", 0, "Sphere", ""}, {PFIELD_FALL_TUBE, "TUBE", 0, "Tube", ""}, - {PFIELD_FALL_CONE, "CONE", 0, "Cone", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem texture_items[] = { - {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, - {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, {PFIELD_TEX_CURL, "CURL", 0, "Curl", ""}, + {PFIELD_TEX_GRAD, "GRADIENT", 0, "Gradient", ""}, + {PFIELD_TEX_RGB, "RGB", 0, "RGB", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem zdirection_items[] = { - {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {PFIELD_Z_POS, "POSITIVE", 0, "+Z", ""}, {PFIELD_Z_NEG, "NEGATIVE", 0, "-Z", ""}, + {PFIELD_Z_BOTH, "BOTH", 0, "Both Z", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem guide_kink_items[] = { - {0, "NONE", 0, "Nothing", ""}, + {0, "NONE", 0, "None", ""}, + {4, "BRAID", 0, "Braid", ""}, {1, "CURL", 0, "Curl", ""}, {2, "RADIAL", 0, "Radial", ""}, - {3, "WAVE", 0, "Wave", ""}, - {4, "BRAID", 0, "Braid", ""}, - {5, "ROTATION", 0, "Rotation", ""}, {6, "ROLL", 0, "Roll", ""}, + {5, "ROTATION", 0, "Rotation", ""}, + {3, "WAVE", 0, "Wave", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 6f2264680c8..a67b0f7c8e6 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -660,7 +660,7 @@ static void rna_ParticleSystem_uv_on_emitter(ParticleSystem *particlesystem, } else { MFace *mface = &modifier->mesh_final->mface[num]; - MTFace *mtface = (MTFace *)CustomData_get_layer_n( + const MTFace *mtface = (const MTFace *)CustomData_get_layer_n( &modifier->mesh_final->fdata, CD_MTFACE, uv_no); psys_interpolate_uvs(&mtface[num], mface->v4, *fuv, r_uv); @@ -694,7 +694,8 @@ static void rna_ParticleSystem_mcol_on_emitter(ParticleSystem *particlesystem, } else { MFace *mface = &modifier->mesh_final->mface[num]; - MCol *mc = (MCol *)CustomData_get_layer_n(&modifier->mesh_final->fdata, CD_MCOL, vcol_no); + const MCol *mc = (const MCol *)CustomData_get_layer_n( + &modifier->mesh_final->fdata, CD_MCOL, vcol_no); MCol mcol; psys_interpolate_mcol(&mc[num * 4], mface->v4, *fuv, &mcol); @@ -1207,19 +1208,19 @@ static int rna_ParticleTarget_name_length(PointerRNA *ptr) return strlen(tstr); } -static int particle_id_check(PointerRNA *ptr) +static int particle_id_check(const PointerRNA *ptr) { - ID *id = ptr->owner_id; + const ID *id = ptr->owner_id; return (GS(id->name) == ID_PA); } -static char *rna_SPHFluidSettings_path(PointerRNA *ptr) +static char *rna_SPHFluidSettings_path(const PointerRNA *ptr) { - SPHFluidSettings *fluid = (SPHFluidSettings *)ptr->data; + const SPHFluidSettings *fluid = (SPHFluidSettings *)ptr->data; if (particle_id_check(ptr)) { - ParticleSettings *part = (ParticleSettings *)ptr->owner_id; + const ParticleSettings *part = (ParticleSettings *)ptr->owner_id; if (part->fluid == fluid) { return BLI_strdup("fluid"); @@ -1462,9 +1463,9 @@ static void psys_vg_name_set__internal(PointerRNA *ptr, const char *value, int i } } -static char *rna_ParticleSystem_path(PointerRNA *ptr) +static char *rna_ParticleSystem_path(const PointerRNA *ptr) { - ParticleSystem *psys = (ParticleSystem *)ptr->data; + const ParticleSystem *psys = (ParticleSystem *)ptr->data; char name_esc[sizeof(psys->name) * 2]; BLI_str_escape(name_esc, psys->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c index 075d660ba2b..4c5dcd5a587 100644 --- a/source/blender/makesrna/intern/rna_pointcloud.c +++ b/source/blender/makesrna/intern/rna_pointcloud.c @@ -27,18 +27,23 @@ # include "WM_api.h" # include "WM_types.h" -static PointCloud *rna_pointcloud(PointerRNA *ptr) +static PointCloud *rna_pointcloud(const PointerRNA *ptr) { return (PointCloud *)ptr->owner_id; } -static int rna_Point_index_get(PointerRNA *ptr) +static int rna_Point_index_get_const(const PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); const float(*co)[3] = ptr->data; return (int)(co - pointcloud->co); } +static int rna_Point_index_get(PointerRNA *ptr) +{ + return rna_Point_index_get_const(ptr); +} + static void rna_Point_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -69,9 +74,9 @@ static void rna_Point_radius_set(PointerRNA *ptr, float value) pointcloud->radius[co - pointcloud->co] = value; } -static char *rna_Point_path(PointerRNA *ptr) +static char *rna_Point_path(const PointerRNA *ptr) { - return BLI_sprintfN("points[%d]", rna_Point_index_get(ptr)); + return BLI_sprintfN("points[%d]", rna_Point_index_get_const(ptr)); } static void rna_PointCloud_update_data(struct Main *UNUSED(bmain), diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index a80e9573657..4108baca2fa 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -108,14 +108,14 @@ static void rna_Pose_IK_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe BIK_clear_data(ob->pose); } -static char *rna_Pose_path(PointerRNA *UNUSED(ptr)) +static char *rna_Pose_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("pose"); } -static char *rna_PoseBone_path(PointerRNA *ptr) +static char *rna_PoseBone_path(const PointerRNA *ptr) { - bPoseChannel *pchan = ptr->data; + const bPoseChannel *pchan = ptr->data; char name_esc[sizeof(pchan->name) * 2]; BLI_str_escape(name_esc, pchan->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 997c110134d..11a7be69f68 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -483,9 +483,10 @@ static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, Point rna_iterator_listbase_begin(iter, &rl->passes, NULL); } -static int rna_RenderPass_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static int rna_RenderPass_rect_get_length(const PointerRNA *ptr, + int length[RNA_MAX_ARRAY_DIMENSION]) { - RenderPass *rpass = (RenderPass *)ptr->data; + const RenderPass *rpass = (RenderPass *)ptr->data; length[0] = rpass->rectx * rpass->recty; length[1] = rpass->channels; diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index c1012f67c5a..0c1fd8cab3c 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -148,7 +148,7 @@ static void rna_RigidBodyWorld_reset(Main *UNUSED(bmain), Scene *UNUSED(scene), BKE_rigidbody_cache_reset(rbw); } -static char *rna_RigidBodyWorld_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyWorld_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("rigidbody_world"); } @@ -240,7 +240,7 @@ static void rna_RigidBodyOb_mesh_source_update(Main *bmain, Scene *scene, Pointe WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } -static char *rna_RigidBodyOb_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyOb_path(const PointerRNA *UNUSED(ptr)) { /* NOTE: this hardcoded path should work as long as only Objects have this */ return BLI_strdup("rigid_body"); @@ -432,7 +432,7 @@ static void rna_RigidBodyOb_angular_damping_set(PointerRNA *ptr, float value) # endif } -static char *rna_RigidBodyCon_path(PointerRNA *UNUSED(ptr)) +static char *rna_RigidBodyCon_path(const PointerRNA *UNUSED(ptr)) { /* NOTE: this hardcoded path should work as long as only Objects have this */ return BLI_strdup("rigid_body_constraint"); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index a7d673d3fe1..72ad695cd03 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -50,56 +50,85 @@ const EnumPropertyItem rna_enum_property_type_items[] = { {0, NULL, 0, NULL, NULL}, }; -/* Keep in sync with RNA_types.h PropertySubType and bpy_props.c's property_subtype_xxx_items */ +/* Wraps multiple enums onto a single line in a way that is difficult to read. + * NOTE: these enums are split up based on their use in `bpy.props` Python module. */ + +/* clang-format off */ +#define RNA_ENUM_PROPERTY_SUBTYPE_STRING_ITEMS \ + {PROP_FILEPATH, "FILE_PATH", 0, "File Path", ""}, \ + {PROP_DIRPATH, "DIR_PATH", 0, "Directory Path", ""}, \ + {PROP_FILENAME, "FILE_NAME", 0, "File Name", ""}, \ + {PROP_BYTESTRING, "BYTE_STRING", 0, "Byte String", ""}, \ + {PROP_PASSWORD, "PASSWORD", 0, "Password", "A string that is displayed hidden ('********')"} + +#define RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ITEMS \ + {PROP_PIXEL, "PIXEL", 0, "Pixel", ""}, \ + {PROP_UNSIGNED, "UNSIGNED", 0, "Unsigned", ""}, \ + {PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""}, \ + {PROP_FACTOR, "FACTOR", 0, "Factor", ""}, \ + {PROP_ANGLE, "ANGLE", 0, "Angle", ""}, \ + {PROP_TIME, "TIME", 0, "Time (Scene Relative)", \ + "Time specified in frames, converted to seconds based on scene frame rate"}, \ + {PROP_TIME_ABSOLUTE, "TIME_ABSOLUTE", 0, "Time (Absolute)", \ + "Time specified in seconds, independent of the scene"}, \ + {PROP_DISTANCE, "DISTANCE", 0, "Distance", ""}, \ + {PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""}, \ + {PROP_POWER, "POWER", 0, "Power", ""}, \ + {PROP_TEMPERATURE, "TEMPERATURE", 0, "Temperature", ""} + +#define RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ARRAY_ITEMS \ + {PROP_COLOR, "COLOR", 0, "Color", ""}, \ + {PROP_TRANSLATION, "TRANSLATION", 0, "Translation", ""}, \ + {PROP_DIRECTION, "DIRECTION", 0, "Direction", ""}, \ + {PROP_VELOCITY, "VELOCITY", 0, "Velocity", ""}, \ + {PROP_ACCELERATION, "ACCELERATION", 0, "Acceleration", ""}, \ + {PROP_MATRIX, "MATRIX", 0, "Matrix", ""}, \ + {PROP_EULER, "EULER", 0, "Euler Angles", ""}, \ + {PROP_QUATERNION, "QUATERNION", 0, "Quaternion", ""}, \ + {PROP_AXISANGLE, "AXISANGLE", 0, "Axis-Angle", ""}, \ + {PROP_XYZ, "XYZ", 0, "XYZ", ""}, \ + {PROP_XYZ_LENGTH, "XYZ_LENGTH", 0, "XYZ Length", ""}, \ + {PROP_COLOR_GAMMA, "COLOR_GAMMA", 0, "Color", ""}, \ + {PROP_COORDS, "COORDS", 0, "Coordinates", ""}, \ + /* Boolean. */ \ + {PROP_LAYER, "LAYER", 0, "Layer", ""}, \ + {PROP_LAYER_MEMBER, "LAYER_MEMBER", 0, "Layer Member", ""} + +/* clang-format on */ + +const EnumPropertyItem rna_enum_property_subtype_string_items[] = { + RNA_ENUM_PROPERTY_SUBTYPE_STRING_ITEMS, + + {PROP_NONE, "NONE", 0, "None", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_property_subtype_number_items[] = { + RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ITEMS, + + {PROP_NONE, "NONE", 0, "None", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_property_subtype_number_array_items[] = { + RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ARRAY_ITEMS, + + {PROP_NONE, "NONE", 0, "None", ""}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_property_subtype_items[] = { {PROP_NONE, "NONE", 0, "None", ""}, - /* strings */ - {PROP_FILEPATH, "FILEPATH", 0, "File Path", ""}, - {PROP_DIRPATH, "DIRPATH", 0, "Directory Path", ""}, - {PROP_FILENAME, "FILENAME", 0, "File Name", ""}, - {PROP_BYTESTRING, "BYTESTRING", 0, "Byte String", ""}, - {PROP_PASSWORD, "PASSWORD", 0, "Password", "A string that is displayed hidden ('********')"}, - - /* numbers */ - {PROP_PIXEL, "PIXEL", 0, "Pixel", ""}, - {PROP_UNSIGNED, "UNSIGNED", 0, "Unsigned", ""}, - {PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""}, - {PROP_FACTOR, "FACTOR", 0, "Factor", ""}, - {PROP_ANGLE, "ANGLE", 0, "Angle", ""}, - {PROP_TIME, - "TIME", - 0, - "Time (Scene Relative)", - "Time specified in frames, converted to seconds based on scene frame rate"}, - {PROP_TIME_ABSOLUTE, - "TIME_ABSOLUTE", - 0, - "Time (Absolute)", - "Time specified in seconds, independent of the scene"}, - {PROP_DISTANCE, "DISTANCE", 0, "Distance", ""}, - {PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""}, - {PROP_POWER, "POWER", 0, "Power", ""}, - {PROP_TEMPERATURE, "TEMPERATURE", 0, "Temperature", ""}, - - /* number arrays */ - {PROP_COLOR, "COLOR", 0, "Color", ""}, - {PROP_TRANSLATION, "TRANSLATION", 0, "Translation", ""}, - {PROP_DIRECTION, "DIRECTION", 0, "Direction", ""}, - {PROP_VELOCITY, "VELOCITY", 0, "Velocity", ""}, - {PROP_ACCELERATION, "ACCELERATION", 0, "Acceleration", ""}, - {PROP_MATRIX, "MATRIX", 0, "Matrix", ""}, - {PROP_EULER, "EULER", 0, "Euler Angles", ""}, - {PROP_QUATERNION, "QUATERNION", 0, "Quaternion", ""}, - {PROP_AXISANGLE, "AXISANGLE", 0, "Axis-Angle", ""}, - {PROP_XYZ, "XYZ", 0, "XYZ", ""}, - {PROP_XYZ_LENGTH, "XYZ_LENGTH", 0, "XYZ Length", ""}, - {PROP_COLOR_GAMMA, "COLOR_GAMMA", 0, "Color", ""}, - {PROP_COORDS, "COORDS", 0, "Coordinates", ""}, - - /* booleans */ - {PROP_LAYER, "LAYER", 0, "Layer", ""}, - {PROP_LAYER_MEMBER, "LAYER_MEMBER", 0, "Layer Member", ""}, + /* String. */ + RNA_ENUM_PROPERTY_SUBTYPE_STRING_ITEMS, + + /* Number. */ + RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ITEMS, + + /* Number array. */ + RNA_ENUM_PROPERTY_SUBTYPE_NUMBER_ARRAY_ITEMS, + {0, NULL, 0, NULL, NULL}, }; @@ -120,6 +149,69 @@ const EnumPropertyItem rna_enum_property_unit_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_property_flag_items[] = { + {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, + {PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", ""}, + {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", ""}, + {PROP_LIB_EXCEPTION, "LIBRARY_EDITABLE", 0, "Library Editable", ""}, + {PROP_PROPORTIONAL, "PROPORTIONAL", 0, "Adjust values proportionally to eachother", ""}, + {PROP_TEXTEDIT_UPDATE, + "TEXTEDIT_UPDATE", + 0, + "Update on every keystroke in textedit 'mode'", + ""}, + {0, NULL, 0, NULL, NULL}, +}; + +/** Only for enum type properties. */ +const EnumPropertyItem rna_enum_property_flag_enum_items[] = { + {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, + {PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", ""}, + {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", ""}, + {PROP_LIB_EXCEPTION, "LIBRARY_EDITABLE", 0, "Library Editable", ""}, + {PROP_ENUM_FLAG, "ENUM_FLAG", 0, "Enum Flag", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_property_override_flag_items[] = { + {PROPOVERRIDE_OVERRIDABLE_LIBRARY, + "LIBRARY_OVERRIDABLE", + 0, + "Library Overridable", + "Make that property editable in library overrides of linked data-blocks"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_property_override_flag_collection_items[] = { + {PROPOVERRIDE_OVERRIDABLE_LIBRARY, + "LIBRARY_OVERRIDABLE", + 0, + "Library Overridable", + "Make that property editable in library overrides of linked data-blocks"}, + {PROPOVERRIDE_NO_PROP_NAME, + "NO_PROPERTY_NAME", + 0, + "No Name", + "Do not use the names of the items, only their indices in the collection"}, + {PROPOVERRIDE_LIBRARY_INSERTION, + "USE_INSERTION", + 0, + "Use Insertion", + "Allow users to add new items in that collection in library overrides"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_property_string_search_flag_items[] = { + {PROP_STRING_SEARCH_SORT, "SORT", 0, "Sort Search Results", ""}, + {PROP_STRING_SEARCH_SUGGESTION, + "SUGGESTION", + 0, + "Suggestion", + "Search results are suggestions (other values may be entered)"}, + + {0, NULL, 0, NULL, NULL}, +}; + /** \} */ #ifdef RNA_RUNTIME @@ -721,7 +813,7 @@ static int rna_IntProperty_default_get(PointerRNA *ptr) return ((IntPropertyRNA *)prop)->defaultvalue; } /* int/float/bool */ -static int rna_NumberProperty_default_array_get_length(PointerRNA *ptr, +static int rna_NumberProperty_default_array_get_length(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { PropertyRNA *prop = (PropertyRNA *)ptr->data; @@ -941,17 +1033,29 @@ static int rna_EnumPropertyItem_identifier_length(PointerRNA *ptr) static void rna_EnumPropertyItem_name_get(PointerRNA *ptr, char *value) { - strcpy(value, ((EnumPropertyItem *)ptr->data)->name); + const EnumPropertyItem *eprop = ptr->data; + /* Name can be NULL in the case of separators + * which are exposed via `_bpy.rna_enum_items_static`. */ + if (eprop->name) { + strcpy(value, eprop->name); + } + else { + value[0] = '\0'; + } } static int rna_EnumPropertyItem_name_length(PointerRNA *ptr) { - return strlen(((EnumPropertyItem *)ptr->data)->name); + const EnumPropertyItem *eprop = ptr->data; + if (eprop->name) { + return strlen(eprop->name); + } + return 0; } static void rna_EnumPropertyItem_description_get(PointerRNA *ptr, char *value) { - EnumPropertyItem *eprop = (EnumPropertyItem *)ptr->data; + const EnumPropertyItem *eprop = ptr->data; if (eprop->description) { strcpy(value, eprop->description); @@ -968,9 +1072,7 @@ static int rna_EnumPropertyItem_description_length(PointerRNA *ptr) if (eprop->description) { return strlen(eprop->description); } - else { - return 0; - } + return 0; } static int rna_EnumPropertyItem_value_get(PointerRNA *ptr) @@ -2655,7 +2757,7 @@ bool rna_property_override_apply_default(Main *bmain, break; } default: - BLI_assert(0); + BLI_assert_unreachable(); return false; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index b647a823929..9b8e94db081 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -345,9 +345,9 @@ const EnumPropertyItem rna_enum_curve_fit_method_items[] = { R_IMF_ENUM_JPEG \ R_IMF_ENUM_JPEG2K \ R_IMF_ENUM_TAGA \ - R_IMF_ENUM_TAGA_RAW{0, "", 0, " ", NULL}, \ - R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR_MULTILAYER R_IMF_ENUM_EXR R_IMF_ENUM_HDR \ - R_IMF_ENUM_TIFF R_IMF_ENUM_WEBP + R_IMF_ENUM_TAGA_RAW \ + RNA_ENUM_ITEM_SEPR_COLUMN, R_IMF_ENUM_CINEON R_IMF_ENUM_DPX R_IMF_ENUM_EXR_MULTILAYER \ + R_IMF_ENUM_EXR R_IMF_ENUM_HDR R_IMF_ENUM_TIFF R_IMF_ENUM_WEBP #ifdef RNA_RUNTIME static const EnumPropertyItem image_only_type_items[] = { @@ -359,11 +359,11 @@ static const EnumPropertyItem image_only_type_items[] = { #endif const EnumPropertyItem rna_enum_image_type_items[] = { - {0, "", 0, N_("Image"), NULL}, + RNA_ENUM_ITEM_HEADING(N_("Image"), NULL), IMAGE_TYPE_ITEMS_IMAGE_ONLY - {0, "", 0, N_("Movie"), NULL}, + RNA_ENUM_ITEM_HEADING(N_("Movie"), NULL), {R_IMF_IMTYPE_AVIJPEG, "AVI_JPEG", ICON_FILE_MOVIE, @@ -1100,12 +1100,12 @@ static void rna_Scene_all_keyingsets_next(CollectionPropertyIterator *iter) iter->valid = (internal->link != NULL); } -static char *rna_SceneEEVEE_path(PointerRNA *UNUSED(ptr)) +static char *rna_SceneEEVEE_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("eevee"); } -static char *rna_SceneGpencil_path(PointerRNA *UNUSED(ptr)) +static char *rna_SceneGpencil_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("grease_pencil_settings"); } @@ -1129,17 +1129,17 @@ static void rna_RenderSettings_stereoViews_begin(CollectionPropertyIterator *ite rna_iterator_listbase_begin(iter, &rd->views, rna_RenderSettings_stereoViews_skip); } -static char *rna_RenderSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_RenderSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render"); } -static char *rna_BakeSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_BakeSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render.bake"); } -static char *rna_ImageFormatSettings_path(PointerRNA *ptr) +static char *rna_ImageFormatSettings_path(const PointerRNA *ptr) { ImageFormatData *imf = (ImageFormatData *)ptr->data; ID *id = ptr->owner_id; @@ -1787,10 +1787,11 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr) rna_Scene_glsl_update(bmain, activescene, ptr); } -static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr) +static char *rna_ViewLayerEEVEE_path(const PointerRNA *ptr) { - ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data; - ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - offsetof(ViewLayer, eevee)); + const ViewLayerEEVEE *view_layer_eevee = (ViewLayerEEVEE *)ptr->data; + const ViewLayer *view_layer = (ViewLayer *)((uint8_t *)view_layer_eevee - + offsetof(ViewLayer, eevee)); char rna_path[sizeof(view_layer->name) * 3]; const size_t view_layer_path_len = rna_ViewLayer_path_buffer_get( @@ -1801,9 +1802,9 @@ static char *rna_ViewLayerEEVEE_path(PointerRNA *ptr) return BLI_strdup(rna_path); } -static char *rna_SceneRenderView_path(PointerRNA *ptr) +static char *rna_SceneRenderView_path(const PointerRNA *ptr) { - SceneRenderView *srv = (SceneRenderView *)ptr->data; + const SceneRenderView *srv = (SceneRenderView *)ptr->data; char srv_name_esc[sizeof(srv->name) * 2]; BLI_str_escape(srv_name_esc, srv->name, sizeof(srv_name_esc)); return BLI_sprintfN("render.views[\"%s\"]", srv_name_esc); @@ -2071,10 +2072,10 @@ static void rna_View3DCursor_matrix_set(PointerRNA *ptr, const float *values) BKE_scene_cursor_from_mat4(cursor, unit_mat, false); } -static char *rna_TransformOrientationSlot_path(PointerRNA *ptr) +static char *rna_TransformOrientationSlot_path(const PointerRNA *ptr) { - Scene *scene = (Scene *)ptr->owner_id; - TransformOrientationSlot *orientation_slot = ptr->data; + const Scene *scene = (Scene *)ptr->owner_id; + const TransformOrientationSlot *orientation_slot = ptr->data; if (!ELEM(NULL, scene, orientation_slot)) { for (int i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { @@ -2085,11 +2086,11 @@ static char *rna_TransformOrientationSlot_path(PointerRNA *ptr) } /* Should not happen, but in case, just return default path. */ - BLI_assert(0); + BLI_assert_unreachable(); return BLI_strdup("transform_orientation_slots[0]"); } -static char *rna_View3DCursor_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DCursor_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("cursor"); } @@ -2188,17 +2189,17 @@ static void rna_UnifiedPaintSettings_radius_update(bContext *C, PointerRNA *ptr) rna_UnifiedPaintSettings_update(C, ptr); } -static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_UnifiedPaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.unified_paint_settings"); } -static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_CurvePaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.curve_paint_settings"); } -static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_SequencerToolSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.sequencer_tool_settings"); } @@ -2222,7 +2223,7 @@ static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr)) } } -static char *rna_MeshStatVis_path(PointerRNA *UNUSED(ptr)) +static char *rna_MeshStatVis_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.statvis"); } @@ -2260,7 +2261,7 @@ static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *UNUSED(scene), SEQ_cache_cleanup((Scene *)ptr->owner_id); } -static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ToolSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings"); } @@ -2701,12 +2702,12 @@ static void rna_UnitSettings_system_update(Main *UNUSED(bmain), } } -static char *rna_UnitSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_UnitSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("unit_settings"); } -static char *rna_FFmpegSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_FFmpegSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render.ffmpeg"); } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 017e8bde7a1..ab5e4c2f0d5 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -259,7 +259,7 @@ static bool rna_ParticleEdit_hair_get(PointerRNA *ptr) return 0; } -static char *rna_ParticleEdit_path(PointerRNA *UNUSED(ptr)) +static char *rna_ParticleEdit_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit"); } @@ -403,15 +403,15 @@ static void rna_Sculpt_ShowMask_update(bContext *C, PointerRNA *UNUSED(ptr)) WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, object); } -static char *rna_Sculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_Sculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.sculpt"); } -static char *rna_VertexPaint_path(PointerRNA *ptr) +static char *rna_VertexPaint_path(const PointerRNA *ptr) { - Scene *scene = (Scene *)ptr->owner_id; - ToolSettings *ts = scene->toolsettings; + const Scene *scene = (Scene *)ptr->owner_id; + const ToolSettings *ts = scene->toolsettings; if (ptr->data == ts->vpaint) { return BLI_strdup("tool_settings.vertex_paint"); } @@ -420,47 +420,47 @@ static char *rna_VertexPaint_path(PointerRNA *ptr) } } -static char *rna_ImagePaintSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_ImagePaintSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.image_paint"); } -static char *rna_PaintModeSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_PaintModeSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.paint_mode"); } -static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_UvSculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.uv_sculpt"); } -static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr)) +static char *rna_CurvesSculpt_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.curves_sculpt"); } -static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint"); } -static char *rna_GpVertexPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpVertexPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_vertex_paint"); } -static char *rna_GpSculptPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpSculptPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt_paint"); } -static char *rna_GpWeightPaint_path(PointerRNA *UNUSED(ptr)) +static char *rna_GpWeightPaint_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_weight_paint"); } -static char *rna_ParticleBrush_path(PointerRNA *UNUSED(ptr)) +static char *rna_ParticleBrush_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit.brush"); } @@ -578,12 +578,12 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) return imapaint->missing_data == 0; } -static char *rna_GPencilSculptSettings_path(PointerRNA *UNUSED(ptr)) +static char *rna_GPencilSculptSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt"); } -static char *rna_GPencilSculptGuide_path(PointerRNA *UNUSED(ptr)) +static char *rna_GPencilSculptGuide_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt.guide"); } diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 51f62b62c8e..8acc31cd71a 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -341,7 +341,7 @@ static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_transform_set_left_handle_frame(seq, value); + SEQ_time_left_handle_frame_set(seq, value); SEQ_transform_fix_single_image_seq_offsets(seq); do_sequence_frame_change_update(scene, seq); SEQ_relations_invalidate_cache_composite(scene, seq); @@ -352,7 +352,7 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_transform_set_right_handle_frame(seq, value); + SEQ_time_right_handle_frame_set(seq, value); SEQ_transform_fix_single_image_seq_offsets(seq); do_sequence_frame_change_update(scene, seq); SEQ_relations_invalidate_cache_composite(scene, seq); @@ -376,24 +376,6 @@ static void rna_Sequence_frame_offset_end_set(PointerRNA *ptr, int value) seq->endofs = value; } -static void rna_Sequence_frame_still_start_set(PointerRNA *ptr, int value) -{ - Sequence *seq = (Sequence *)ptr->data; - Scene *scene = (Scene *)ptr->owner_id; - - SEQ_relations_invalidate_cache_composite(scene, seq); - seq->startstill = value; -} - -static void rna_Sequence_frame_still_end_set(PointerRNA *ptr, int value) -{ - Sequence *seq = (Sequence *)ptr->data; - Scene *scene = (Scene *)ptr->owner_id; - - SEQ_relations_invalidate_cache_composite(scene, seq); - seq->endstill = value; -} - static void rna_Sequence_anim_startofs_final_set(PointerRNA *ptr, int value) { Sequence *seq = (Sequence *)ptr->data; @@ -455,7 +437,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + value); + SEQ_time_right_handle_frame_set(seq, SEQ_time_left_handle_frame_get(seq) + value); do_sequence_frame_change_update(scene, seq); SEQ_relations_invalidate_cache_composite(scene, seq); } @@ -463,7 +445,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value) static int rna_Sequence_frame_length_get(PointerRNA *ptr) { Sequence *seq = (Sequence *)ptr->data; - return SEQ_transform_get_right_handle_frame(seq) - SEQ_transform_get_left_handle_frame(seq); + return SEQ_time_right_handle_frame_get(seq) - SEQ_time_left_handle_frame_get(seq); } static int rna_Sequence_frame_editable(PointerRNA *ptr, const char **UNUSED(r_info)) @@ -522,13 +504,13 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor return data.seq; } -static char *rna_SequenceTransform_path(PointerRNA *ptr) +static char *rna_SequenceTransform_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_transform(ed, ptr->data); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -574,13 +556,13 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop) return data.seq; } -static char *rna_SequenceCrop_path(PointerRNA *ptr) +static char *rna_SequenceCrop_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_crop(ed, ptr->data); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -718,22 +700,17 @@ static StructRNA *rna_Sequence_refine(struct PointerRNA *ptr) } } -static char *rna_Sequence_path(PointerRNA *ptr) +static char *rna_Sequence_path(const PointerRNA *ptr) { - Sequence *seq = (Sequence *)ptr->data; + const Sequence *seq = (Sequence *)ptr->data; /* sequencer data comes from scene... * TODO: would be nice to make SequenceEditor data a data-block of its own (for shorter paths) */ - if (seq->name + 2) { - char name_esc[(sizeof(seq->name) - 2) * 2]; + char name_esc[(sizeof(seq->name) - 2) * 2]; - BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); - return BLI_sprintfN("sequence_editor.sequences_all[\"%s\"]", name_esc); - } - else { - return BLI_strdup(""); - } + BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); + return BLI_sprintfN("sequence_editor.sequences_all[\"%s\"]", name_esc); } static IDProperty **rna_Sequence_idprops(PointerRNA *ptr) @@ -1057,14 +1034,14 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed, return data.seq; } -static char *rna_SequenceColorBalance_path(PointerRNA *ptr) +static char *rna_SequenceColorBalance_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; SequenceModifierData *smd; Editing *ed = SEQ_editing_get(scene); Sequence *seq = sequence_get_by_colorbalance(ed, ptr->data, &smd); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc)); @@ -1201,14 +1178,14 @@ static StructRNA *rna_SequenceModifier_refine(struct PointerRNA *ptr) } } -static char *rna_SequenceModifier_path(PointerRNA *ptr) +static char *rna_SequenceModifier_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); SequenceModifierData *smd = ptr->data; Sequence *seq = sequence_get_by_modifier(ed, smd); - if (seq && seq->name + 2) { + if (seq) { char name_esc[(sizeof(seq->name) - 2) * 2]; char name_esc_smd[sizeof(smd->name) * 2]; @@ -1419,7 +1396,7 @@ static void rna_SequenceTimelineChannel_name_set(PointerRNA *ptr, const char *va sizeof(channel->name)); } -static char *rna_SeqTimelineChannel_path(PointerRNA *ptr) +static char *rna_SeqTimelineChannel_path(const PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene); @@ -1802,33 +1779,33 @@ static void rna_def_strip_color_balance(BlenderRNA *brna) static const EnumPropertyItem blend_mode_items[] = { {SEQ_BLEND_REPLACE, "REPLACE", 0, "Replace", ""}, {SEQ_TYPE_CROSS, "CROSS", 0, "Cross", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_DARKEN, "DARKEN", 0, "Darken", ""}, {SEQ_TYPE_MUL, "MULTIPLY", 0, "Multiply", ""}, {SEQ_TYPE_COLOR_BURN, "BURN", 0, "Color Burn", ""}, {SEQ_TYPE_LINEAR_BURN, "LINEAR_BURN", 0, "Linear Burn", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_LIGHTEN, "LIGHTEN", 0, "Lighten", ""}, {SEQ_TYPE_SCREEN, "SCREEN", 0, "Screen", ""}, {SEQ_TYPE_DODGE, "DODGE", 0, "Color Dodge", ""}, {SEQ_TYPE_ADD, "ADD", 0, "Add", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_OVERLAY, "OVERLAY", 0, "Overlay", ""}, {SEQ_TYPE_SOFT_LIGHT, "SOFT_LIGHT", 0, "Soft Light", ""}, {SEQ_TYPE_HARD_LIGHT, "HARD_LIGHT", 0, "Hard Light", ""}, {SEQ_TYPE_VIVID_LIGHT, "VIVID_LIGHT", 0, "Vivid Light", ""}, {SEQ_TYPE_LIN_LIGHT, "LINEAR_LIGHT", 0, "Linear Light", ""}, {SEQ_TYPE_PIN_LIGHT, "PIN_LIGHT", 0, "Pin Light", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""}, {SEQ_TYPE_EXCLUSION, "EXCLUSION", 0, "Exclusion", ""}, {SEQ_TYPE_SUB, "SUBTRACT", 0, "Subtract", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_HUE, "HUE", 0, "Hue", ""}, {SEQ_TYPE_SATURATION, "SATURATION", 0, "Saturation", ""}, {SEQ_TYPE_BLEND_COLOR, "COLOR", 0, "Color", ""}, {SEQ_TYPE_VALUE, "VALUE", 0, "Value", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_ALPHAOVER, "ALPHA_OVER", 0, "Alpha Over", ""}, {SEQ_TYPE_ALPHAUNDER, "ALPHA_UNDER", 0, "Alpha Under", ""}, {SEQ_TYPE_GAMCROSS, "GAMMA_CROSS", 0, "Gamma Cross", ""}, @@ -2037,22 +2014,6 @@ static void rna_def_sequence(BlenderRNA *brna) prop, NULL, "rna_Sequence_frame_offset_end_set", "rna_Sequence_frame_offset_end_range"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); - prop = RNA_def_property(srna, "frame_still_start", PROP_INT, PROP_TIME); - RNA_def_property_int_sdna(prop, NULL, "startstill"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ - RNA_def_property_range(prop, 0, MAXFRAME); - RNA_def_property_ui_text(prop, "Start Still", ""); - RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_start_set", NULL); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); - - prop = RNA_def_property(srna, "frame_still_end", PROP_INT, PROP_TIME); - RNA_def_property_int_sdna(prop, NULL, "endstill"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ - RNA_def_property_range(prop, 0, MAXFRAME); - RNA_def_property_ui_text(prop, "End Still", ""); - RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_end_set", NULL); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); - prop = RNA_def_property(srna, "channel", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "machine"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -3203,23 +3164,23 @@ static void rna_def_color_mix(StructRNA *srna) {SEQ_TYPE_MUL, "MULTIPLY", 0, "Multiply", ""}, {SEQ_TYPE_COLOR_BURN, "BURN", 0, "Color Burn", ""}, {SEQ_TYPE_LINEAR_BURN, "LINEAR_BURN", 0, "Linear Burn", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_LIGHTEN, "LIGHTEN", 0, "Lighten", ""}, {SEQ_TYPE_SCREEN, "SCREEN", 0, "Screen", ""}, {SEQ_TYPE_DODGE, "DODGE", 0, "Color Dodge", ""}, {SEQ_TYPE_ADD, "ADD", 0, "Add", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_OVERLAY, "OVERLAY", 0, "Overlay", ""}, {SEQ_TYPE_SOFT_LIGHT, "SOFT_LIGHT", 0, "Soft Light", ""}, {SEQ_TYPE_HARD_LIGHT, "HARD_LIGHT", 0, "Hard Light", ""}, {SEQ_TYPE_VIVID_LIGHT, "VIVID_LIGHT", 0, "Vivid Light", ""}, {SEQ_TYPE_LIN_LIGHT, "LINEAR_LIGHT", 0, "Linear Light", ""}, {SEQ_TYPE_PIN_LIGHT, "PIN_LIGHT", 0, "Pin Light", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""}, {SEQ_TYPE_EXCLUSION, "EXCLUSION", 0, "Exclusion", ""}, {SEQ_TYPE_SUB, "SUBTRACT", 0, "Subtract", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {SEQ_TYPE_HUE, "HUE", 0, "Hue", ""}, {SEQ_TYPE_SATURATION, "SATURATION", 0, "Saturation", ""}, {SEQ_TYPE_BLEND_COLOR, "COLOR", 0, "Color", ""}, diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c index cefa445740d..0a656222fd5 100644 --- a/source/blender/makesrna/intern/rna_shader_fx.c +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -142,9 +142,9 @@ static void rna_ShaderFx_name_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(NULL, "shader_effects", oldname, gmd->name); } -static char *rna_ShaderFx_path(PointerRNA *ptr) +static char *rna_ShaderFx_path(const PointerRNA *ptr) { - ShaderFxData *gmd = ptr->data; + const ShaderFxData *gmd = ptr->data; char name_esc[sizeof(gmd->name) * 2]; BLI_str_escape(name_esc, gmd->name, sizeof(name_esc)); diff --git a/source/blender/makesrna/intern/rna_sound.c b/source/blender/makesrna/intern/rna_sound.c index 3d51f80adde..2714b4157fd 100644 --- a/source/blender/makesrna/intern/rna_sound.c +++ b/source/blender/makesrna/intern/rna_sound.c @@ -12,6 +12,22 @@ #include "DNA_sound_types.h" +#include "BKE_sound.h" + +/* Enumeration for Audio Channels, compatible with eSoundChannels */ +const EnumPropertyItem rna_enum_audio_channels_items[] = { + {SOUND_CHANNELS_INVALID, "INVALID", ICON_NONE, "Invalid", "Invalid"}, + {SOUND_CHANNELS_MONO, "MONO", ICON_NONE, "Mono", "Mono"}, + {SOUND_CHANNELS_STEREO, "STEREO", ICON_NONE, "Stereo", "Stereo"}, + {SOUND_CHANNELS_STEREO_LFE, "STEREO_LFE", ICON_NONE, "Stereo LFE", "Stereo FX"}, + {SOUND_CHANNELS_SURROUND4, "CHANNELS_4", ICON_NONE, "4 Channels", "4 Channels"}, + {SOUND_CHANNELS_SURROUND5, "CHANNELS_5", ICON_NONE, "5 Channels", "5 Channels"}, + {SOUND_CHANNELS_SURROUND51, "SURROUND_51", ICON_NONE, "5.1 Surround", "5.1 Surround"}, + {SOUND_CHANNELS_SURROUND61, "SURROUND_61", ICON_NONE, "6.1 Surround", "6.1 Surround"}, + {SOUND_CHANNELS_SURROUND71, "SURROUND_71", ICON_NONE, "7.1 Surround", "7.1 Surround"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "BKE_context.h" @@ -70,6 +86,18 @@ static void rna_def_sound(BlenderRNA *brna) "If the file contains multiple audio channels they are rendered to a single one"); RNA_def_property_update(prop, 0, "rna_Sound_update"); + prop = RNA_def_property(srna, "samplerate", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "samplerate"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Samplerate", "Samplerate of the audio in Hz"); + + prop = RNA_def_property(srna, "channels", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "audio_channels"); + RNA_def_property_enum_items(prop, rna_enum_audio_channels_items); + RNA_def_property_enum_default(prop, SOUND_CHANNELS_INVALID); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Audio channels", "Definition of audio channels"); + RNA_api_sound(srna); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index fb3fa69139a..af43133c979 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -87,8 +87,8 @@ const EnumPropertyItem rna_enum_space_type_items[] = { /* empty must be here for python, is skipped for UI */ {SPACE_EMPTY, "EMPTY", ICON_NONE, "Empty", ""}, - /* General */ - {0, "", ICON_NONE, "General", ""}, + /* General. */ + RNA_ENUM_ITEM_HEADING("General", NULL), {SPACE_VIEW3D, "VIEW_3D", ICON_VIEW3D, @@ -107,8 +107,8 @@ const EnumPropertyItem rna_enum_space_type_items[] = { {SPACE_SEQ, "SEQUENCE_EDITOR", ICON_SEQUENCE, "Video Sequencer", "Video editing tools"}, {SPACE_CLIP, "CLIP_EDITOR", ICON_TRACKER, "Movie Clip Editor", "Motion tracking tools"}, - /* Animation */ - {0, "", ICON_NONE, "Animation", ""}, + /* Animation. */ + RNA_ENUM_ITEM_HEADING("Animation", NULL), #if 0 {SPACE_ACTION, "TIMELINE", @@ -124,8 +124,8 @@ const EnumPropertyItem rna_enum_space_type_items[] = { "Edit drivers and keyframe interpolation"}, {SPACE_NLA, "NLA_EDITOR", ICON_NLA, "Nonlinear Animation", "Combine and layer Actions"}, - /* Scripting */ - {0, "", ICON_NONE, "Scripting", ""}, + /* Scripting. */ + RNA_ENUM_ITEM_HEADING("Scripting", NULL), {SPACE_TEXT, "TEXT_EDITOR", ICON_TEXT, @@ -152,8 +152,8 @@ const EnumPropertyItem rna_enum_space_type_items[] = { "Global bar at the bottom of the " "screen for general status information"}, - /* Data */ - {0, "", ICON_NONE, "Data", ""}, + /* Data. */ + RNA_ENUM_ITEM_HEADING("Data", NULL), {SPACE_OUTLINER, "OUTLINER", ICON_OUTLINER, @@ -435,28 +435,28 @@ static const EnumPropertyItem rna_enum_studio_light_items[] = { }; static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] = { - {0, "", ICON_NONE, "General", ""}, + RNA_ENUM_ITEM_HEADING("General", NULL), {EEVEE_RENDER_PASS_COMBINED, "COMBINED", 0, "Combined", ""}, {EEVEE_RENDER_PASS_EMIT, "EMISSION", 0, "Emission", ""}, {EEVEE_RENDER_PASS_ENVIRONMENT, "ENVIRONMENT", 0, "Environment", ""}, {EEVEE_RENDER_PASS_AO, "AO", 0, "Ambient Occlusion", ""}, {EEVEE_RENDER_PASS_SHADOW, "SHADOW", 0, "Shadow", ""}, - {0, "", ICON_NONE, "Light", ""}, + RNA_ENUM_ITEM_HEADING("Light", NULL), {EEVEE_RENDER_PASS_DIFFUSE_LIGHT, "DIFFUSE_LIGHT", 0, "Diffuse Light", ""}, {EEVEE_RENDER_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""}, {EEVEE_RENDER_PASS_SPECULAR_LIGHT, "SPECULAR_LIGHT", 0, "Specular Light", ""}, {EEVEE_RENDER_PASS_SPECULAR_COLOR, "SPECULAR_COLOR", 0, "Specular Color", ""}, {EEVEE_RENDER_PASS_VOLUME_LIGHT, "VOLUME_LIGHT", 0, "Volume Light", ""}, - {0, "", ICON_NONE, "Effects", ""}, + RNA_ENUM_ITEM_HEADING("Effects", NULL), {EEVEE_RENDER_PASS_BLOOM, "BLOOM", 0, "Bloom", ""}, - {0, "", ICON_NONE, "Data", ""}, + RNA_ENUM_ITEM_HEADING("Data", NULL), {EEVEE_RENDER_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {EEVEE_RENDER_PASS_MIST, "MIST", 0, "Mist", ""}, - {0, "", ICON_NONE, "Shader AOV", ""}, + RNA_ENUM_ITEM_HEADING("Shader AOV", NULL), {EEVEE_RENDER_PASS_AOV, "AOV", 0, "AOV", ""}, {0, NULL, 0, NULL, NULL}, @@ -1565,7 +1565,7 @@ static int rna_SpaceView3D_icon_from_show_object_viewport_get(PointerRNA *ptr) &v3d->object_type_exclude_select); } -static char *rna_View3DShading_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DShading_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("shading"); } @@ -1575,7 +1575,7 @@ static PointerRNA rna_SpaceView3D_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_View3DOverlay, ptr->data); } -static char *rna_View3DOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_View3DOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } @@ -1587,12 +1587,12 @@ static PointerRNA rna_SpaceImage_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_SpaceImageOverlay, ptr->data); } -static char *rna_SpaceImageOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceImageOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } -static char *rna_SpaceUVEditor_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceUVEditor_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("uv_editor"); } @@ -2024,7 +2024,7 @@ static void rna_SpaceProperties_context_update(Main *UNUSED(bmain), } } -static int rna_SpaceProperties_tab_search_results_getlength(PointerRNA *ptr, +static int rna_SpaceProperties_tab_search_results_getlength(const PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { SpaceProperties *sbuts = ptr->data; @@ -2426,12 +2426,12 @@ static void rna_Sequencer_view_type_update(Main *UNUSED(bmain), ED_area_tag_refresh(area); } -static char *rna_SpaceSequencerPreviewOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceSequencerPreviewOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("preview_overlay"); } -static char *rna_SpaceSequencerTimelineOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceSequencerTimelineOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("timeline_overlay"); } @@ -2442,7 +2442,7 @@ static PointerRNA rna_SpaceNode_overlay_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_SpaceNodeOverlay, ptr->data); } -static char *rna_SpaceNodeOverlay_path(PointerRNA *UNUSED(ptr)) +static char *rna_SpaceNodeOverlay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("overlay"); } @@ -2635,7 +2635,7 @@ static void rna_SpaceClipEditor_view_type_update(Main *UNUSED(bmain), /* File browser. */ -static char *rna_FileSelectParams_path(PointerRNA *UNUSED(ptr)) +static char *rna_FileSelectParams_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("params"); } @@ -3338,7 +3338,7 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ {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, + ICON_GEOMETRY_NODES, "Geometry", "Show meshes, curves, lattice, armatures and metaballs data"}, {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, @@ -5461,6 +5461,17 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Show Mask Editor", "Show Mask editing related properties"); + /* Gizmo Toggles. */ + prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE); + RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + + prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SI_GIZMO_HIDE_NAVIGATE); + RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + /* Overlays */ prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 6f7ee966723..3b28dc70e9e 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -98,22 +98,22 @@ const EnumPropertyItem rna_enum_texture_type_items[] = { #ifndef RNA_RUNTIME static const EnumPropertyItem blend_type_items[] = { {MTEX_BLEND, "MIX", 0, "Mix", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MTEX_DARK, "DARKEN", 0, "Darken", ""}, {MTEX_MUL, "MULTIPLY", 0, "Multiply", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MTEX_LIGHT, "LIGHTEN", 0, "Lighten", ""}, {MTEX_SCREEN, "SCREEN", 0, "Screen", ""}, {MTEX_ADD, "ADD", 0, "Add", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MTEX_OVERLAY, "OVERLAY", 0, "Overlay", ""}, {MTEX_SOFT_LIGHT, "SOFT_LIGHT", 0, "Soft Light", ""}, {MTEX_LIN_LIGHT, "LINEAR_LIGHT", 0, "Linear Light", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MTEX_DIFF, "DIFFERENCE", 0, "Difference", ""}, {MTEX_SUB, "SUBTRACT", 0, "Subtract", ""}, {MTEX_DIV, "DIVIDE", 0, "Divide", ""}, - {0, "", ICON_NONE, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MTEX_BLEND_HUE, "HUE", 0, "Hue", ""}, {MTEX_BLEND_SAT, "SATURATION", 0, "Saturation", ""}, {MTEX_BLEND_COLOR, "COLOR", 0, "Color", ""}, @@ -291,7 +291,7 @@ void rna_TextureSlot_update(bContext *C, PointerRNA *ptr) } } -char *rna_TextureSlot_path(PointerRNA *ptr) +char *rna_TextureSlot_path(const PointerRNA *ptr) { MTex *mtex = ptr->data; diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index e604469e3fd..b9acd57430b 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -41,7 +41,7 @@ # include "WM_api.h" -static char *rna_tracking_path(PointerRNA *UNUSED(ptr)) +static char *rna_tracking_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking"); } @@ -72,7 +72,7 @@ static void rna_tracking_defaultSettings_searchUpdate(Main *UNUSED(bmain), } } -static char *rna_trackingTrack_path(PointerRNA *ptr) +static char *rna_trackingTrack_path(const PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; MovieTrackingTrack *track = (MovieTrackingTrack *)ptr->data; @@ -255,7 +255,7 @@ static void rna_trackingPlaneMarker_frame_set(PointerRNA *ptr, int value) } } -static char *rna_trackingPlaneTrack_path(PointerRNA *ptr) +static char *rna_trackingPlaneTrack_path(const PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->owner_id; MovieTrackingPlaneTrack *plane_track = (MovieTrackingPlaneTrack *)ptr->data; @@ -289,7 +289,7 @@ static void rna_trackingPlaneTrack_name_set(PointerRNA *ptr, const char *value) } } -static char *rna_trackingCamera_path(PointerRNA *UNUSED(ptr)) +static char *rna_trackingCamera_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking.camera"); } @@ -321,7 +321,7 @@ static void rna_trackingCamera_focal_mm_set(PointerRNA *ptr, float value) } } -static char *rna_trackingStabilization_path(PointerRNA *UNUSED(ptr)) +static char *rna_trackingStabilization_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tracking.stabilization"); } @@ -557,7 +557,7 @@ static void rna_tracking_markerPattern_update(Main *UNUSED(bmain), { MovieTrackingMarker *marker = (MovieTrackingMarker *)ptr->data; - BKE_tracking_marker_clamp(marker, CLAMP_PAT_DIM); + BKE_tracking_marker_clamp_search_size(marker); } static void rna_tracking_markerSearch_update(Main *UNUSED(bmain), @@ -566,7 +566,7 @@ static void rna_tracking_markerSearch_update(Main *UNUSED(bmain), { MovieTrackingMarker *marker = (MovieTrackingMarker *)ptr->data; - BKE_tracking_marker_clamp(marker, CLAMP_SEARCH_DIM); + BKE_tracking_marker_clamp_search_size(marker); } static void rna_tracking_markerPattern_boundbox_get(PointerRNA *ptr, float *values) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6f57e2755ee..43e8879fc17 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -62,23 +62,23 @@ const EnumPropertyItem rna_enum_preference_section_items[] = { {USER_SECTION_LIGHT, "LIGHTS", 0, "Lights", ""}, {USER_SECTION_EDITING, "EDITING", 0, "Editing", ""}, {USER_SECTION_ANIMATION, "ANIMATION", 0, "Animation", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {USER_SECTION_ADDONS, "ADDONS", 0, "Add-ons", ""}, #if 0 /* def WITH_USERDEF_WORKSPACES */ - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {USER_SECTION_WORKSPACE_CONFIG, "WORKSPACE_CONFIG", 0, "Configuration File", ""}, {USER_SECTION_WORKSPACE_ADDONS, "WORKSPACE_ADDONS", 0, "Add-on Overrides", ""}, {USER_SECTION_WORKSPACE_KEYMAPS, "WORKSPACE_KEYMAPS", 0, "Keymap Overrides", ""}, #endif - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {USER_SECTION_INPUT, "INPUT", 0, "Input", ""}, {USER_SECTION_NAVIGATION, "NAVIGATION", 0, "Navigation", ""}, {USER_SECTION_KEYMAP, "KEYMAP", 0, "Keymap", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {USER_SECTION_SYSTEM, "SYSTEM", 0, "System", ""}, {USER_SECTION_SAVE_LOAD, "SAVE_LOAD", 0, "Save & Load", ""}, {USER_SECTION_FILE_PATHS, "FILE_PATHS", 0, "File Paths", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {USER_SECTION_EXPERIMENTAL, "EXPERIMENTAL", 0, "Experimental", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -1101,6 +1101,16 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char return GPU_mem_stats_supported() ? PROP_EDITABLE : 0; } +static int rna_userdef_experimental_use_new_curve_tools_editable(struct PointerRNA *UNUSED(ptr), + const char **r_info) +{ + if (U.experimental.use_new_curves_type) { + return PROP_EDITABLE; + } + *r_info = "Only available when new curves type is enabled"; + return 0; +} + #else # define USERDEF_TAG_DIRTY_PROPERTY_UPDATE_ENABLE \ @@ -6394,6 +6404,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_type", 1); RNA_def_property_ui_text(prop, "New Curves Type", "Enable the new curves data type in the UI"); + prop = RNA_def_property(srna, "use_new_curves_tools", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_new_curves_tools", 1); + RNA_def_property_editable_func(prop, "rna_userdef_experimental_use_new_curve_tools_editable"); + RNA_def_property_ui_text( + prop, "New Curves Tools", "Enable additional features for the new curves data block"); + prop = RNA_def_property(srna, "use_cycles_debug", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1); RNA_def_property_ui_text(prop, "Cycles Debug", "Enable Cycles debugging options for developers"); diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 12cb35b239d..6d4ea18fc38 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -46,12 +46,12 @@ const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { # include "WM_api.h" # include "WM_types.h" -static char *rna_VolumeRender_path(PointerRNA *UNUSED(ptr)) +static char *rna_VolumeRender_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("render"); } -static char *rna_VolumeDisplay_path(PointerRNA *UNUSED(ptr)) +static char *rna_VolumeDisplay_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("display"); } @@ -485,6 +485,21 @@ static void rna_def_volume_render(BlenderRNA *brna) RNA_def_struct_sdna(srna, "VolumeRender"); RNA_def_struct_path_func(srna, "rna_VolumeRender_path"); + static const EnumPropertyItem precision_items[] = { + {VOLUME_PRECISION_FULL, "FULL", 0, "Full", "Full float (Use 32 bit for all data)"}, + {VOLUME_PRECISION_HALF, "HALF", 0, "Half", "Half float (Use 16 bit for all data)"}, + {VOLUME_PRECISION_VARIABLE, "VARIABLE", 0, "Variable", "Use variable bit quantization"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "precision", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, precision_items); + RNA_def_property_ui_text(prop, + "Precision", + "Specify volume data precision. Lower values reduce memory consumption " + "at the cost of detail"); + RNA_def_property_update(prop, 0, "rna_Volume_update_display"); + static const EnumPropertyItem space_items[] = { {VOLUME_SPACE_OBJECT, "OBJECT", diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b09a9ab0733..1dc2cbe9e69 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -36,16 +36,16 @@ static const EnumPropertyItem event_mouse_type_items[] = { {BUTTON5MOUSE, "BUTTON5MOUSE", 0, "Button5", ""}, {BUTTON6MOUSE, "BUTTON6MOUSE", 0, "Button6", ""}, {BUTTON7MOUSE, "BUTTON7MOUSE", 0, "Button7", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {TABLET_STYLUS, "PEN", 0, "Pen", ""}, {TABLET_ERASER, "ERASER", 0, "Eraser", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MOUSEMOVE, "MOUSEMOVE", 0, "Move", ""}, {MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", ""}, {MOUSEZOOM, "TRACKPADZOOM", 0, "Mouse/Trackpad Zoom", ""}, {MOUSEROTATE, "MOUSEROTATE", 0, "Mouse/Trackpad Rotate", ""}, {MOUSESMARTZOOM, "MOUSESMARTZOOM", 0, "Mouse/Trackpad Smart Zoom", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {WHEELUPMOUSE, "WHEELUPMOUSE", 0, "Wheel Up", ""}, {WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, "Wheel Down", ""}, {WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", ""}, @@ -135,22 +135,22 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {BUTTON5MOUSE, "BUTTON5MOUSE", 0, "Button5 Mouse", "MB5"}, {BUTTON6MOUSE, "BUTTON6MOUSE", 0, "Button6 Mouse", "MB6"}, {BUTTON7MOUSE, "BUTTON7MOUSE", 0, "Button7 Mouse", "MB7"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {TABLET_STYLUS, "PEN", 0, "Pen", ""}, {TABLET_ERASER, "ERASER", 0, "Eraser", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {MOUSEMOVE, "MOUSEMOVE", 0, "Mouse Move", "MsMov"}, {INBETWEEN_MOUSEMOVE, "INBETWEEN_MOUSEMOVE", 0, "In-between Move", "MsSubMov"}, {MOUSEPAN, "TRACKPADPAN", 0, "Mouse/Trackpad Pan", "MsPan"}, {MOUSEZOOM, "TRACKPADZOOM", 0, "Mouse/Trackpad Zoom", "MsZoom"}, {MOUSEROTATE, "MOUSEROTATE", 0, "Mouse/Trackpad Rotate", "MsRot"}, {MOUSESMARTZOOM, "MOUSESMARTZOOM", 0, "Mouse/Trackpad Smart Zoom", "MsSmartZoom"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {WHEELUPMOUSE, "WHEELUPMOUSE", 0, "Wheel Up", "WhUp"}, {WHEELDOWNMOUSE, "WHEELDOWNMOUSE", 0, "Wheel Down", "WhDown"}, {WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", "WhIn"}, {WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, "Wheel Out", "WhOut"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {EVT_AKEY, "A", 0, "A", ""}, {EVT_BKEY, "B", 0, "B", ""}, {EVT_CKEY, "C", 0, "C", ""}, @@ -177,7 +177,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {EVT_XKEY, "X", 0, "X", ""}, {EVT_YKEY, "Y", 0, "Y", ""}, {EVT_ZKEY, "Z", 0, "Z", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {EVT_ZEROKEY, "ZERO", 0, "0", ""}, {EVT_ONEKEY, "ONE", 0, "1", ""}, {EVT_TWOKEY, "TWO", 0, "2", ""}, @@ -188,14 +188,14 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {EVT_SEVENKEY, "SEVEN", 0, "7", ""}, {EVT_EIGHTKEY, "EIGHT", 0, "8", ""}, {EVT_NINEKEY, "NINE", 0, "9", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {EVT_LEFTCTRLKEY, "LEFT_CTRL", 0, "Left Ctrl", "CtrlL"}, {EVT_LEFTALTKEY, "LEFT_ALT", 0, "Left Alt", "AltL"}, {EVT_LEFTSHIFTKEY, "LEFT_SHIFT", 0, "Left Shift", "ShiftL"}, {EVT_RIGHTALTKEY, "RIGHT_ALT", 0, "Right Alt", "AltR"}, {EVT_RIGHTCTRLKEY, "RIGHT_CTRL", 0, "Right Ctrl", "CtrlR"}, {EVT_RIGHTSHIFTKEY, "RIGHT_SHIFT", 0, "Right Shift", "ShiftR"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {EVT_OSKEY, "OSKEY", 0, "OS Key", "Cmd"}, {EVT_APPKEY, "APP", 0, "Application", "App"}, {EVT_GRLESSKEY, "GRLESS", 0, "Grless", ""}, @@ -268,14 +268,14 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {EVT_PAGEUPKEY, "PAGE_UP", 0, "Page Up", "PgUp"}, {EVT_PAGEDOWNKEY, "PAGE_DOWN", 0, "Page Down", "PgDown"}, {EVT_ENDKEY, "END", 0, "End", ""}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {EVT_MEDIAPLAY, "MEDIA_PLAY", 0, "Media Play/Pause", ">/||"}, {EVT_MEDIASTOP, "MEDIA_STOP", 0, "Media Stop", "Stop"}, {EVT_MEDIAFIRST, "MEDIA_FIRST", 0, "Media First", "|<<"}, {EVT_MEDIALAST, "MEDIA_LAST", 0, "Media Last", ">>|"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {KM_TEXTINPUT, "TEXTINPUT", 0, "Text Input", "TxtIn"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {WINDEACTIVATE, "WINDOW_DEACTIVATE", 0, "Window Deactivate", ""}, {TIMER, "TIMER", 0, "Timer", "Tmr"}, {TIMER0, "TIMER0", 0, "Timer 0", "Tmr0"}, @@ -285,7 +285,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {TIMERAUTOSAVE, "TIMER_AUTOSAVE", 0, "Timer Autosave", "TmrSave"}, {TIMERREPORT, "TIMER_REPORT", 0, "Timer Report", "TmrReport"}, {TIMERREGION, "TIMERREGION", 0, "Timer Region", "TmrReg"}, - {0, "", 0, NULL, NULL}, + RNA_ENUM_ITEM_SEPR, {NDOF_MOTION, "NDOF_MOTION", 0, "NDOF Motion", "NdofMov"}, /* buttons on all 3dconnexion devices */ {NDOF_BUTTON_MENU, "NDOF_BUTTON_MENU", 0, "NDOF Menu", "NdofMenu"}, @@ -896,8 +896,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value) } } -/* assumes value to be an enum from rna_enum_event_type_items */ -/* function makes sure keymodifiers are only valid keys, ESC keeps it unaltered */ +/** + * Assumes value to be an enum from rna_enum_event_type_items. + * Function makes sure keymodifiers are only valid keys, ESC keeps it unaltered. + */ static void rna_wmKeyMapItem_keymodifier_set(PointerRNA *ptr, int value) { wmKeyMapItem *kmi = ptr->data; @@ -1155,9 +1157,7 @@ static int rna_wmKeyMapItem_idname_length(PointerRNA *ptr) { wmKeyMapItem *kmi = ptr->data; char pyname[OP_MAX_TYPENAME]; - - WM_operator_py_idname(pyname, kmi->idname); - return strlen(pyname); + return WM_operator_py_idname(pyname, kmi->idname); } static void rna_wmKeyMapItem_idname_set(PointerRNA *ptr, const char *value) @@ -2599,6 +2599,9 @@ static void rna_def_keyconfig(BlenderRNA *brna) "rna_wmKeyMapItem_idname_get", "rna_wmKeyMapItem_idname_length", "rna_wmKeyMapItem_idname_set"); + RNA_def_property_string_search_func(prop, + "WM_operatortype_idname_visit_for_search", + PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION); RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index a04b29b8815..dcfa1bbca51 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -849,7 +849,7 @@ bool rna_XrSessionState_active_action_set_set(bContext *C, const char *action_se { # ifdef WITH_XR_OPENXR wmWindowManager *wm = CTX_wm_manager(C); - return WM_xr_active_action_set_set(&wm->xr, action_set_name); + return WM_xr_active_action_set_set(&wm->xr, action_set_name, true); # else UNUSED_VARS(C, action_set_name); return false; @@ -1196,6 +1196,50 @@ static int rna_XrEventData_action_length(PointerRNA *ptr) # endif } +static void rna_XrEventData_user_path_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_user_path_other_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->user_path_other); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_user_path_other_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->user_path_other); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrEventData_type_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -2402,6 +2446,19 @@ static void rna_def_xr_eventdata(BlenderRNA *brna) prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); RNA_def_property_ui_text(prop, "Action", "XR action name"); + prop = RNA_def_property(srna, "user_path", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_get", "rna_XrEventData_user_path_length", NULL); + RNA_def_property_ui_text(prop, "User Path", "User path of the action. E.g. \"/user/hand/left\""); + + prop = RNA_def_property(srna, "user_path_other", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_user_path_other_get", "rna_XrEventData_user_path_other_length", NULL); + RNA_def_property_ui_text( + prop, "User Path Other", "Other user path, for bimanual actions. E.g. \"/user/hand/right\""); + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_items(prop, rna_enum_xr_action_types); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a5e5bf36dcd..1aac3c2191d 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC intern/MOD_normal_edit.c intern/MOD_ocean.c intern/MOD_particleinstance.c - intern/MOD_particlesystem.c + intern/MOD_particlesystem.cc intern/MOD_remesh.c intern/MOD_screw.c intern/MOD_shapekey.c diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index a7364af10a3..c634873cfe4 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -39,7 +39,6 @@ #include "BLO_read_write.h" -#include "BKE_curveprofile.h" #include "bmesh.h" #include "bmesh_tools.h" @@ -396,10 +395,12 @@ static void panelRegister(ARegionType *region_type) region_type, "shading", "Shading", NULL, shading_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const BevelModifierData *bmd = (const BevelModifierData *)md; + BLO_write_struct(writer, BevelModifierData, bmd); + if (bmd->custom_profile) { BKE_curveprofile_blend_write(writer, bmd->custom_profile); } diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 52162eaacc5..8b6c306dae8 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -798,12 +798,25 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_CorrectiveSmooth, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - const CorrectiveSmoothModifierData *csmd = (const CorrectiveSmoothModifierData *)md; + CorrectiveSmoothModifierData csmd = *(const CorrectiveSmoothModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + csmd.bind_coords_num = 0; + csmd.bind_coords = NULL; + } + } - if (csmd->bind_coords) { - BLO_write_float3_array(writer, csmd->bind_coords_num, (float *)csmd->bind_coords); + BLO_write_struct_at_address(writer, CorrectiveSmoothModifierData, md, &csmd); + + if (csmd.bind_coords != NULL) { + BLO_write_float3_array(writer, csmd.bind_coords_num, (float *)csmd.bind_coords); } } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 7ad7d6eef3d..149cf0c0cbb 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -310,13 +310,11 @@ static void displaceModifier_do(DisplaceModifierData *dmd, CustomData *ldata = &mesh->ldata; if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) { - float(*clnors)[3] = NULL; - if (!CustomData_has_layer(ldata, CD_NORMAL)) { BKE_mesh_calc_normals_split(mesh); } - clnors = CustomData_get_layer(ldata, CD_NORMAL); + float(*clnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); vert_clnors = MEM_malloc_arrayN(verts_num, sizeof(*vert_clnors), __func__); BKE_mesh_normals_loop_to_vertex( verts_num, mesh->mloop, mesh->totloop, (const float(*)[3])clnors, vert_clnors); diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index 9e2bb79138e..d76a750f7e8 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -124,7 +124,7 @@ static void createFacepa(ExplodeModifierData *emd, ParticleSystemModifierData *p /* set protected verts */ if (emd->vgroup) { - MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); if (dvert) { const int defgrp_index = emd->vgroup - 1; for (i = 0; i < totvert; i++, dvert++) { @@ -911,7 +911,6 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, int totdup = 0, totvert = 0, totface = 0, totpart = 0, delface = 0; int i, v, u; uint ed_v1, ed_v2, mindex = 0; - MTFace *mtface = NULL, *mtf; totface = mesh->totface; totvert = mesh->totvert; @@ -977,7 +976,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* the final duplicated vertices */ explode = BKE_mesh_new_nomain_from_template(mesh, totdup, 0, totface - delface, 0, 0); - mtface = CustomData_get_layer_named(&explode->fdata, CD_MTFACE, emd->uvname); + MTFace *mtface = CustomData_get_layer_named(&explode->fdata, CD_MTFACE, emd->uvname); /* getting back to object space */ invert_m4_m4(imat, ctx->object->obmat); @@ -1086,7 +1085,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* Clamp to this range to avoid flipping to the other side of the coordinates. */ CLAMP(age, 0.001f, 0.999f); - mtf = mtface + u; + MTFace *mtf = mtface + u; mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = age; mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = 0.5f; diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 1000bbf45d6..3649ece12e1 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -519,10 +519,12 @@ static void panelRegister(ARegionType *region_type) region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const HookModifierData *hmd = (const HookModifierData *)md; + BLO_write_struct(writer, HookModifierData, hmd); + if (hmd->curfalloff) { BKE_curvemapping_blend_write(writer, hmd->curfalloff); } diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index 239cb7f5a5a..06ded1c4488 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -843,11 +843,26 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_LaplacianDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + LaplacianDeformModifierData lmd = *(const LaplacianDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + lmd.verts_num = 0; + lmd.vertexco = NULL; + } + } - BLO_write_float3_array(writer, lmd->verts_num, lmd->vertexco); + BLO_write_struct_at_address(writer, LaplacianDeformModifierData, md, &lmd); + + if (lmd.vertexco != NULL) { + BLO_write_float3_array(writer, lmd.verts_num, lmd.vertexco); + } } static void blendRead(BlendDataReader *reader, ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 900dee98268..0813901fc49 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -91,7 +91,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } /* A vertex will be in the mask if a selected bone influences it more than a certain threshold. */ -static void compute_vertex_mask__armature_mode(MDeformVert *dvert, +static void compute_vertex_mask__armature_mode(const MDeformVert *dvert, Mesh *mesh, Object *armature_ob, float threshold, @@ -125,7 +125,7 @@ static void compute_vertex_mask__armature_mode(MDeformVert *dvert, } /* A vertex will be in the mask if the vertex group influences it more than a certain threshold. */ -static void compute_vertex_mask__vertex_group_mode(MDeformVert *dvert, +static void compute_vertex_mask__vertex_group_mode(const MDeformVert *dvert, int defgrp_index, float threshold, MutableSpan<bool> r_vertex_mask) @@ -347,7 +347,7 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, } static float get_interp_factor_from_vgroup( - MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2) + const MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2) { /* NOTE: this calculation is done twice for every vertex, * instead of storing it the first time and then reusing it. */ @@ -360,7 +360,7 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<bool> vertex_mask, Span<int> vertex_map, - MDeformVert *dvert, + const MDeformVert *dvert, int defgrp_index, float threshold, uint edges_masked_num, @@ -478,7 +478,7 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh, Span<bool> vertex_mask, Span<int> vertex_map, Span<int> edge_map, - MDeformVert *dvert, + const MDeformVert *dvert, int defgrp_index, float threshold, Span<int> masked_poly_indices, @@ -619,7 +619,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) (mmd->flag & MOD_MASK_SMOOTH); /* Return empty or input mesh when there are no vertex groups. */ - MDeformVert *dvert = (MDeformVert *)CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = (const MDeformVert *)CustomData_get_layer(&mesh->vdata, + CD_MDEFORMVERT); if (dvert == nullptr) { return invert_mask ? mesh : BKE_mesh_new_nomain_from_template(mesh, 0, 0, 0, 0, 0); } diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index c2e9e5ebe7d..0cff85d30ec 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -581,26 +581,49 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_MeshDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - MeshDeformModifierData *mmd = (MeshDeformModifierData *)md; - int size = mmd->dyngridsize; + MeshDeformModifierData mmd = *(const MeshDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + mmd.influences_num = 0; + mmd.bindinfluences = NULL; + mmd.verts_num = 0; + mmd.bindoffsets = NULL; + mmd.cage_verts_num = 0; + mmd.bindcagecos = NULL; + mmd.dyngridsize = 0; + mmd.dyngrid = NULL; + mmd.influences_num = 0; + mmd.dyninfluences = NULL; + mmd.dynverts = NULL; + } + } - BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->bindinfluences); + const int size = mmd.dyngridsize; + + BLO_write_struct_at_address(writer, MeshDeformModifierData, md, &mmd); + + BLO_write_struct_array(writer, MDefInfluence, mmd.influences_num, mmd.bindinfluences); /* NOTE: `bindoffset` is abusing `verts_num + 1` as its size, this becomes an incorrect value in * case `verts_num == 0`, since `bindoffset` is then NULL, not a size 1 allocated array. */ - if (mmd->verts_num > 0) { - BLO_write_int32_array(writer, mmd->verts_num + 1, mmd->bindoffsets); + if (mmd.verts_num > 0) { + BLO_write_int32_array(writer, mmd.verts_num + 1, mmd.bindoffsets); } else { - BLI_assert(mmd->bindoffsets == NULL); + BLI_assert(mmd.bindoffsets == NULL); } - BLO_write_float3_array(writer, mmd->cage_verts_num, mmd->bindcagecos); - BLO_write_struct_array(writer, MDefCell, size * size * size, mmd->dyngrid); - BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->dyninfluences); - BLO_write_int32_array(writer, mmd->verts_num, mmd->dynverts); + BLO_write_float3_array(writer, mmd.cage_verts_num, mmd.bindcagecos); + BLO_write_struct_array(writer, MDefCell, size * size * size, mmd.dyngrid); + BLO_write_struct_array(writer, MDefInfluence, mmd.influences_num, mmd.dyninfluences); + BLO_write_int32_array(writer, mmd.verts_num, mmd.dynverts); } static void blendRead(BlendDataReader *reader, ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 998fb0a94a3..273050eafd8 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -5,8 +5,9 @@ */ #include <cstring> +#include <limits> -#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -15,7 +16,6 @@ #include "DNA_cachefile_types.h" #include "DNA_defaults.h" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -42,6 +42,8 @@ #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" +#include "GEO_mesh_primitive_cuboid.hh" + #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" @@ -104,40 +106,17 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return (mcmd->cache_file == nullptr) || (mcmd->object_path[0] == '\0'); } -static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) +static Mesh *generate_bounding_box_mesh(const Mesh *org_mesh) { - const BoundBox *bb = BKE_object_boundbox_get(object); - Mesh *result = BKE_mesh_new_nomain_from_template(org_mesh, 8, 0, 0, 24, 6); - - MVert *mvert = result->mvert; - for (int i = 0; i < 8; ++i) { - copy_v3_v3(mvert[i].co, bb->vec[i]); - } - - /* See DNA_object_types.h for the diagram showing the order of the vertices for a BoundBox. */ - static unsigned int loops_v[6][4] = { - {0, 4, 5, 1}, - {4, 7, 6, 5}, - {7, 3, 2, 6}, - {3, 0, 1, 2}, - {1, 5, 6, 2}, - {3, 7, 4, 0}, - }; - - MLoop *mloop = result->mloop; - for (int i = 0; i < 6; ++i) { - for (int j = 0; j < 4; ++j, ++mloop) { - mloop->v = loops_v[i][j]; - } - } - - MPoly *mpoly = result->mpoly; - for (int i = 0; i < 6; ++i) { - mpoly[i].loopstart = i * 4; - mpoly[i].totloop = 4; + using namespace blender; + float3 min(std::numeric_limits<float>::max()); + float3 max(-std::numeric_limits<float>::max()); + if (!BKE_mesh_minmax(org_mesh, min, max)) { + return nullptr; } - BKE_mesh_calc_edges(result, false, false); + Mesh *result = geometry::create_cuboid_mesh(max - min, 2, 2, 2); + BKE_mesh_translate(result, math::midpoint(min, max), false); return result; } @@ -170,7 +149,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Do not process data if using a render procedural, return a box instead for displaying in the * viewport. */ if (BKE_cache_file_uses_render_procedural(cache_file, scene)) { - return generate_bounding_box_mesh(ctx->object, org_mesh); + return generate_bounding_box_mesh(org_mesh); } /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 21041e8e1b2..c1e16e1b8e5 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -45,6 +45,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_pointcloud.h" @@ -396,8 +397,9 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket) */ static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index) { - BLI_assert(node_tree.field_inferencing_interface != nullptr); - const FieldInferencingInterface &field_interface = *node_tree.field_inferencing_interface; + BLI_assert(node_tree.runtime->field_inferencing_interface); + const FieldInferencingInterface &field_interface = + *node_tree.runtime->field_inferencing_interface; return field_interface.inputs[socket_index] != InputSocketFieldType::None; } @@ -753,6 +755,7 @@ static void initialize_group_input(NodesModifierData &nmd, { const bNodeSocketType &socket_type = *socket.typeinfo(); const bNodeSocket &bsocket = *socket.bsocket(); + const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(bsocket.type); if (nmd.settings.properties == nullptr) { socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; @@ -769,8 +772,7 @@ static void initialize_group_input(NodesModifierData &nmd, } if (!input_has_attribute_toggle(*nmd.node_group, socket.index())) { - init_socket_cpp_value_from_property( - *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value); + init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; } @@ -779,14 +781,17 @@ static void initialize_group_input(NodesModifierData &nmd, const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( nmd.settings.properties, (socket.identifier() + attribute_name_suffix).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { - init_socket_cpp_value_from_property( - *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value); + init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; } const bool use_attribute = IDP_Int(property_use_attribute) != 0; if (use_attribute) { const StringRef attribute_name{IDP_String(property_attribute_name)}; + if (!blender::bke::allow_procedural_attribute_access(attribute_name)) { + init_socket_cpp_value_from_property(*property, socket_data_type, r_value); + return; + } auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>( attribute_name, *socket_type.base_cpp_type); GField attribute_field{std::move(attribute_input), 0}; @@ -797,8 +802,7 @@ static void initialize_group_input(NodesModifierData &nmd, cpp_type->construct_from_field(r_value, std::move(attribute_field)); } else { - init_socket_cpp_value_from_property( - *property, static_cast<eNodeSocketDatatype>(bsocket.type), r_value); + init_socket_cpp_value_from_property(*property, socket_data_type, r_value); } } @@ -944,6 +948,9 @@ static MultiValueMap<AttributeDomain, OutputAttributeInfo> find_output_attribute if (attribute_name.is_empty()) { continue; } + if (!blender::bke::allow_procedural_attribute_access(attribute_name)) { + continue; + } const int index = socket->index(); const GPointer value = output_values[index]; @@ -985,17 +992,16 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store( if (!component.attribute_domain_supported(domain)) { continue; } - const int domain_size = component.attribute_domain_size(domain); + const int domain_num = component.attribute_domain_num(domain); blender::bke::GeometryComponentFieldContext field_context{component, domain}; - blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_num}; for (const OutputAttributeInfo &output_info : outputs_info) { const CPPType &type = output_info.field.cpp_type(); OutputAttributeToStore store{ component_type, domain, output_info.name, - GMutableSpan{ - type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}}; + GMutableSpan{type, MEM_malloc_arrayN(domain_num, type.size(), __func__), domain_num}}; field_evaluator.add_with_destination(output_info.field, store.data); attributes_to_store.append(store); } @@ -1430,6 +1436,14 @@ static void add_attribute_search_button(const bContext &C, nullptr, attribute_search_exec_fn, nullptr); + + char *attribute_name = RNA_string_get_alloc( + md_ptr, rna_path_attribute_name.c_str(), nullptr, 0, nullptr); + const bool access_allowed = blender::bke::allow_procedural_attribute_access(attribute_name); + MEM_freeN(attribute_name); + if (!access_allowed) { + UI_but_flag_enable(but, UI_BUT_REDALERT); + } } static void add_attribute_search_or_value_buttons(const bContext &C, @@ -1730,9 +1744,12 @@ static void panelRegister(ARegionType *region_type) panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + + BLO_write_struct(writer, NodesModifierData, nmd); + if (nmd->settings.properties != nullptr) { /* Note that the property settings are based on the socket type info * and don't necessarily need to be written, but we can't just free them. */ @@ -1799,7 +1816,7 @@ ModifierTypeInfo modifierType_Nodes = { eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), - /* icon */ ICON_NODETREE, + /* icon */ ICON_GEOMETRY_NODES, /* copyData */ copyData, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index fe05f48a868..c215ac601a1 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -136,7 +136,7 @@ static void mix_normals(const float mix_factor, if (dvert) { facs = MEM_malloc_arrayN((size_t)loops_num, sizeof(*facs), __func__); BKE_defvert_extract_vgroup_to_loopweights( - dvert, defgrp_index, verts_num, mloop, loops_num, facs, use_invert_vgroup); + dvert, defgrp_index, verts_num, mloop, loops_num, use_invert_vgroup, facs); } for (i = loops_num, no_new = nos_new, no_old = nos_old, wfac = facs; i--; @@ -532,14 +532,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, MDeformVert *dvert; float(*loopnors)[3] = NULL; - short(*clnors)[2] = NULL; CustomData *ldata = &result->ldata; 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); + short(*clnors)[2] = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); if (use_current_clnors) { clnors = CustomData_duplicate_referenced_layer(ldata, CD_CUSTOMLOOPNORMAL, loops_num); loopnors = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loopnors), __func__); diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.cc index 032227307e7..c6a606360e3 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -5,8 +5,8 @@ * \ingroup modifiers */ -#include <stddef.h> -#include <string.h> +#include <cstddef> +#include <cstring> #include "BLI_utildefines.h" @@ -51,11 +51,11 @@ static void freeData(ModifierData *md) ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } psmd->totdmvert = psmd->totdmedge = psmd->totdmface = 0; @@ -81,8 +81,8 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla * code has to be called then to ensure proper remapping of that pointer. See e.g. * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ - tpsmd->mesh_final = NULL; - tpsmd->mesh_original = NULL; + tpsmd->mesh_final = nullptr; + tpsmd->mesh_original = nullptr; tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0; } @@ -104,7 +104,7 @@ static void deformVerts(ModifierData *md, { Mesh *mesh_src = mesh; ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - ParticleSystem *psys = NULL; + ParticleSystem *psys = nullptr; if (ctx->object->particlesystem.first) { psys = psmd->psys; @@ -117,28 +117,28 @@ static void deformVerts(ModifierData *md, return; } - if (mesh_src == NULL) { + if (mesh_src == nullptr) { mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, NULL, vertexCos, verts_num, false, true); - if (mesh_src == NULL) { + ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + if (mesh_src == nullptr) { return; } } /* clear old dm */ - bool had_mesh_final = (psmd->mesh_final != NULL); + bool had_mesh_final = (psmd->mesh_final != nullptr); if (psmd->mesh_final) { - BKE_id_free(NULL, psmd->mesh_final); - psmd->mesh_final = NULL; + BKE_id_free(nullptr, psmd->mesh_final); + psmd->mesh_final = nullptr; if (psmd->mesh_original) { - BKE_id_free(NULL, psmd->mesh_original); - psmd->mesh_original = NULL; + BKE_id_free(nullptr, psmd->mesh_original); + psmd->mesh_original = nullptr; } } else if (psmd->flag & eParticleSystemFlag_file_loaded) { /* in file read mesh just wasn't saved in file so no need to reset everything */ psmd->flag &= ~eParticleSystemFlag_file_loaded; - if (psys->particles == NULL) { + if (psys->particles == nullptr) { psys->recalc |= ID_RECALC_PSYS_RESET; } /* TODO(sergey): This is not how particles were working prior to copy on @@ -165,18 +165,18 @@ static void deformVerts(ModifierData *md, /* Get the original mesh from the object, this is what the particles * are attached to so in case of non-deform modifiers we need to remap * them to the final mesh (typically subdivision surfaces). */ - Mesh *mesh_original = NULL; + Mesh *mesh_original = nullptr; if (ctx->object->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ctx->object); if (em) { /* In edit mode get directly from the edit mesh. */ - psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, mesh); + psmd->mesh_original = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, mesh); } else { /* Otherwise get regular mesh. */ - mesh_original = ctx->object->data; + mesh_original = static_cast<Mesh *>(ctx->object->data); } } else { @@ -193,8 +193,8 @@ static void deformVerts(ModifierData *md, BKE_mesh_tessface_ensure(psmd->mesh_original); } - if (!ELEM(mesh_src, NULL, mesh, psmd->mesh_final)) { - BKE_id_free(NULL, mesh_src); + if (!ELEM(mesh_src, nullptr, mesh, psmd->mesh_final)) { + BKE_id_free(nullptr, mesh_src); } /* Report change in mesh structure. @@ -221,7 +221,7 @@ static void deformVerts(ModifierData *md, if (DEG_is_active(ctx->depsgraph)) { Object *object_orig = DEG_get_original_object(ctx->object); ModifierData *md_orig = BKE_modifiers_findby_name(object_orig, psmd->modifier.name); - BLI_assert(md_orig != NULL); + BLI_assert(md_orig != nullptr); ParticleSystemModifierData *psmd_orig = (ParticleSystemModifierData *)md_orig; psmd_orig->flag = psmd->flag; } @@ -237,16 +237,16 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - const bool do_temp_mesh = (mesh == NULL); + const bool do_temp_mesh = (mesh == nullptr); if (do_temp_mesh) { mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name); - BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0})); + BM_mesh_bm_to_me(nullptr, editData->bm, mesh, &((BMeshToMeshParams){0})); } deformVerts(md, ob, mesh, vertexCos, verts_num); if (derivedData) { - BKE_id_free(NULL, mesh); + BKE_id_free(nullptr, mesh); } } #endif @@ -258,7 +258,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - Object *ob = ob_ptr.data; + Object *ob = static_cast<Object *>(ob_ptr.data); ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; @@ -291,8 +291,8 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) { ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - psmd->mesh_final = NULL; - psmd->mesh_original = NULL; + psmd->mesh_final = nullptr; + psmd->mesh_original = nullptr; /* This is written as part of ob->particlesystem. */ BLO_read_data_address(reader, &psmd->psys); psmd->flag &= ~eParticleSystemFlag_psys_updated; @@ -315,23 +315,23 @@ ModifierTypeInfo modifierType_ParticleSystem = { /* copyData */ copyData, /* deformVerts */ deformVerts, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ NULL, - /* modifyGeometrySet */ NULL, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ nullptr, + /* modifyGeometrySet */ nullptr, /* initData */ initData, /* requiredDataMask */ requiredDataMask, /* freeData */ freeData, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, + /* blendWrite */ nullptr, /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 08925e8aeb1..0e22f59c2fb 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -182,7 +182,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * ScrewModifierData *ltmd = (ScrewModifierData *)md; const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER) != 0; - int *origindex; int mpoly_index = 0; uint step; uint i, j; @@ -375,6 +374,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * uv_u_scale = (uv_u_scale / (float)ltmd->iter) * (angle / ((float)M_PI * 2.0f)); } + /* The `screw_ofs` cannot change from now on. */ + const bool do_remove_doubles = (ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f); + /* Only calculate normals if `do_remove_doubles` since removing doubles frees the normals. */ + const bool do_normal_create = (ltmd->flag & MOD_SCREW_NORMAL_CALC) && + (do_remove_doubles == false); + result = BKE_mesh_new_nomain_from_template( mesh, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys); @@ -383,7 +388,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * medge_orig = mesh->medge; mvert_new = result->mvert; - float(*vert_normals_new)[3] = BKE_mesh_vertex_normals_for_write(result); mpoly_new = result->mpoly; mloop_new = result->mloop; medge_new = result->medge; @@ -392,7 +396,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * CustomData_add_layer(&result->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys); } - origindex = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); + int *origindex = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)totvert); @@ -472,7 +476,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } + float(*vert_normals_new)[3] = do_normal_create ? BKE_mesh_vertex_normals_for_write(result) : + NULL; + if (ltmd->flag & MOD_SCREW_NORMAL_CALC) { + /* * Normal Calculation (for face flipping) * Sort edge verts for correct face flipping @@ -766,68 +774,69 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * * calculate vertex normals that can be propagated on lathing * use edge connectivity work this out */ - if (SV_IS_VALID(vc->v[0])) { - if (SV_IS_VALID(vc->v[1])) { - /* 2 edges connected. */ - /* make 2 connecting vert locations relative to the middle vert */ - sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); - sub_v3_v3v3(tmp_vec2, mvert_new[vc->v[1]].co, mvert_new[i].co); - /* normalize so both edges have the same influence, no matter their length */ - normalize_v3(tmp_vec1); - normalize_v3(tmp_vec2); - - /* vc_no_tmp1 - this line is the average direction of both connecting edges - * - * Use the edge order to make the subtraction, flip the normal the right way - * edge should be there but check just in case... */ - if (vc->e[0]->v1 == i) { - sub_v3_v3(tmp_vec1, tmp_vec2); - } - else { - sub_v3_v3v3(tmp_vec1, tmp_vec2, tmp_vec1); - } - } - else { - /* only 1 edge connected - same as above except - * don't need to average edge direction */ - if (vc->e[0]->v2 == i) { - sub_v3_v3v3(tmp_vec1, mvert_new[i].co, mvert_new[vc->v[0]].co); + if (do_normal_create) { + if (SV_IS_VALID(vc->v[0])) { + if (SV_IS_VALID(vc->v[1])) { + /* 2 edges connected. */ + /* make 2 connecting vert locations relative to the middle vert */ + sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + sub_v3_v3v3(tmp_vec2, mvert_new[vc->v[1]].co, mvert_new[i].co); + /* normalize so both edges have the same influence, no matter their length */ + normalize_v3(tmp_vec1); + normalize_v3(tmp_vec2); + + /* vc_no_tmp1 - this line is the average direction of both connecting edges + * + * Use the edge order to make the subtraction, flip the normal the right way + * edge should be there but check just in case... */ + if (vc->e[0]->v1 == i) { + sub_v3_v3(tmp_vec1, tmp_vec2); + } + else { + sub_v3_v3v3(tmp_vec1, tmp_vec2, tmp_vec1); + } } else { - sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + /* only 1 edge connected - same as above except + * don't need to average edge direction */ + if (vc->e[0]->v2 == i) { + sub_v3_v3v3(tmp_vec1, mvert_new[i].co, mvert_new[vc->v[0]].co); + } + else { + sub_v3_v3v3(tmp_vec1, mvert_new[vc->v[0]].co, mvert_new[i].co); + } } - } - /* tmp_vec2 - is a line 90d from the pivot to the vec - * This is used so the resulting normal points directly away from the middle */ - cross_v3_v3v3(tmp_vec2, axis_vec, vc->co); + /* tmp_vec2 - is a line 90d from the pivot to the vec + * This is used so the resulting normal points directly away from the middle */ + cross_v3_v3v3(tmp_vec2, axis_vec, vc->co); - if (UNLIKELY(is_zero_v3(tmp_vec2))) { - /* we're _on_ the axis, so copy it based on our winding */ - if (vc->e[0]->v2 == i) { - negate_v3_v3(vc->no, axis_vec); + if (UNLIKELY(is_zero_v3(tmp_vec2))) { + /* we're _on_ the axis, so copy it based on our winding */ + if (vc->e[0]->v2 == i) { + negate_v3_v3(vc->no, axis_vec); + } + else { + copy_v3_v3(vc->no, axis_vec); + } } else { - copy_v3_v3(vc->no, axis_vec); + /* edge average vector and right angle to the pivot make the normal */ + cross_v3_v3v3(vc->no, tmp_vec1, tmp_vec2); } } else { - /* edge average vector and right angle to the pivot make the normal */ - cross_v3_v3v3(vc->no, tmp_vec1, tmp_vec2); + copy_v3_v3(vc->no, vc->co); } - } - else { - copy_v3_v3(vc->no, vc->co); - } - - /* we won't be looping on this data again so copy normals here */ - if ((angle < 0.0f) != do_flip) { - negate_v3(vc->no); - } - normalize_v3(vc->no); - copy_v3_v3(vert_normals_new[i], vc->no); + /* we won't be looping on this data again so copy normals here */ + if ((angle < 0.0f) != do_flip) { + negate_v3(vc->no); + } + normalize_v3(vc->no); + copy_v3_v3(vert_normals_new[i], vc->no); + } /* Done with normals */ } } @@ -846,7 +855,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * for (step = 1; step < step_tot; step++) { const uint varray_stride = totvert * step; float step_angle; - float nor_tx[3]; float mat[4][4]; /* Rotation Matrix */ step_angle = (angle / (float)(step_tot - (!close))) * (float)step; @@ -872,10 +880,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * for (j = 0; j < totvert; j++, mv_new_base++, mv_new++) { /* set normal */ if (vert_connect) { - mul_v3_m3v3(nor_tx, mat3, vert_connect[j].no); - - /* set the normal now its transformed */ - copy_v3_v3(vert_normals_new[mv_new - mvert_new], nor_tx); + if (do_normal_create) { + /* Set the normal now its transformed. */ + mul_v3_m3v3(vert_normals_new[mv_new - mvert_new], mat3, vert_connect[j].no); + } } /* set location */ @@ -1118,11 +1126,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_freeN(vert_loop_map); } - if ((ltmd->flag & MOD_SCREW_NORMAL_CALC)) { + if (do_normal_create) { BKE_mesh_vertex_normals_clear_dirty(result); } - if ((ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f)) { + if (do_remove_doubles) { result = mesh_remove_doubles_on_axis(result, mvert_new, totvert, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 7f96dcb82fb..5f238209015 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -881,34 +881,29 @@ static int calc_edge_subdivisions(const MVert *mvert, /* Take a Mesh and subdivide its edges to keep skin nodes * reasonably close. */ -static Mesh *subdivide_base(Mesh *orig) +static Mesh *subdivide_base(const Mesh *orig) { - Mesh *result; - MVertSkin *orignode, *outnode; - MVert *origvert, *outvert; - MEdge *origedge, *outedge, *e; - MDeformVert *origdvert, *outdvert; - int orig_vert_num, orig_edge_num; - int subd_num, *degree, *edge_subd; + const MEdge *e; + int subd_num; int i, j, k, u, v; float radrat; - orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); - origvert = orig->mvert; - origedge = orig->medge; - origdvert = orig->dvert; - orig_vert_num = orig->totvert; - orig_edge_num = orig->totedge; + const MVertSkin *orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN); + const MVert *origvert = orig->mvert; + const MEdge *origedge = orig->medge; + const MDeformVert *origdvert = orig->dvert; + int orig_vert_num = orig->totvert; + int orig_edge_num = orig->totedge; /* Get degree of all vertices */ - degree = MEM_calloc_arrayN(orig_vert_num, sizeof(int), "degree"); + int *degree = MEM_calloc_arrayN(orig_vert_num, sizeof(int), "degree"); for (i = 0; i < orig_edge_num; i++) { degree[origedge[i].v1]++; degree[origedge[i].v2]++; } /* Per edge, store how many subdivisions are needed */ - edge_subd = MEM_calloc_arrayN((uint)orig_edge_num, sizeof(int), "edge_subd"); + int *edge_subd = MEM_calloc_arrayN((uint)orig_edge_num, sizeof(int), "edge_subd"); for (i = 0, subd_num = 0; i < orig_edge_num; i++) { edge_subd[i] += calc_edge_subdivisions(origvert, orignode, &origedge[i], degree); BLI_assert(edge_subd[i] >= 0); @@ -918,13 +913,13 @@ static Mesh *subdivide_base(Mesh *orig) MEM_freeN(degree); /* Allocate output mesh */ - result = BKE_mesh_new_nomain_from_template( + Mesh *result = BKE_mesh_new_nomain_from_template( orig, orig_vert_num + subd_num, orig_edge_num + subd_num, 0, 0, 0); - outvert = result->mvert; - outedge = result->medge; - outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); - outdvert = result->dvert; + MVert *outvert = result->mvert; + MEdge *outedge = result->medge; + MVertSkin *outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN); + MDeformVert *outdvert = result->dvert; /* Copy original vertex data */ CustomData_copy_data(&orig->vdata, &result->vdata, 0, 0, orig_vert_num); diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 48154a3670d..1f0aee7d689 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -75,7 +75,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * case MOD_SOLIDIFY_MODE_NONMANIFOLD: return MOD_solidify_nonmanifold_modifyMesh(md, ctx, mesh); default: - BLI_assert(0); + BLI_assert_unreachable(); } return mesh; } diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index a80918b8d2b..9b0012e3890 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1166,8 +1166,8 @@ static bool surfacedeformBind(Object *ob, SurfaceDeformModifierData *smd_eval, float (*vertexCos)[3], uint verts_num, - uint tpolys_num, - uint tverts_num, + uint target_polys_num, + uint target_verts_num, Mesh *target, Mesh *mesh) { @@ -1182,7 +1182,7 @@ static bool surfacedeformBind(Object *ob, SDefAdjacency *adj_array; SDefEdgePolys *edge_polys; - vert_edges = MEM_calloc_arrayN(tverts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); + vert_edges = MEM_calloc_arrayN(target_verts_num, sizeof(*vert_edges), "SDefVertEdgeMap"); if (vert_edges == NULL) { BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory"); return false; @@ -1220,7 +1220,7 @@ static bool surfacedeformBind(Object *ob, } adj_result = buildAdjacencyMap( - mpoly, medge, mloop, tpolys_num, tedges_num, vert_edges, adj_array, edge_polys); + mpoly, medge, mloop, target_polys_num, tedges_num, vert_edges, adj_array, edge_polys); if (adj_result == MOD_SDEF_BIND_RESULT_NONMANY_ERR) { BKE_modifier_set_error( @@ -1233,7 +1233,8 @@ static bool surfacedeformBind(Object *ob, } smd_orig->mesh_verts_num = verts_num; - smd_orig->polys_num = tpolys_num; + smd_orig->target_verts_num = target_verts_num; + smd_orig->target_polys_num = target_polys_num; int defgrp_index; MDeformVert *dvert; @@ -1249,7 +1250,8 @@ static bool surfacedeformBind(Object *ob, .medge = medge, .mloop = mloop, .looptri = BKE_mesh_runtime_looptri_ensure(target), - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetBindVertArray"), + .targetCos = MEM_malloc_arrayN( + target_verts_num, sizeof(float[3]), "SDefTargetBindVertArray"), .bind_verts = smd_orig->verts, .vertexCos = vertexCos, .falloff = smd_orig->falloff, @@ -1268,7 +1270,7 @@ static bool surfacedeformBind(Object *ob, invert_m4_m4(data.imat, smd_orig->mat); - for (int i = 0; i < tverts_num; i++) { + for (int i = 0; i < target_verts_num; i++) { mul_v3_m4v3(data.targetCos[i], smd_orig->mat, mvert[i].co); } @@ -1431,7 +1433,7 @@ static void surfacedeformModifier_do(ModifierData *md, { SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md; Mesh *target; - uint tverts_num, tpolys_num; + uint target_verts_num, target_polys_num; /* Exit function if bind flag is not set (free bind data if any). */ if (!(smd->flags & MOD_SDEF_BIND)) { @@ -1453,8 +1455,8 @@ static void surfacedeformModifier_do(ModifierData *md, return; } - tverts_num = BKE_mesh_wrapper_vert_len(target); - tpolys_num = BKE_mesh_wrapper_poly_len(target); + target_verts_num = BKE_mesh_wrapper_vert_len(target); + target_polys_num = BKE_mesh_wrapper_poly_len(target); /* If not bound, execute bind. */ if (smd->verts == NULL) { @@ -1473,25 +1475,62 @@ static void surfacedeformModifier_do(ModifierData *md, /* Avoid converting edit-mesh data, binding is an exception. */ BKE_mesh_wrapper_ensure_mdata(target); - if (!surfacedeformBind( - ob, smd_orig, smd, vertexCos, verts_num, tpolys_num, tverts_num, target, mesh)) { + if (!surfacedeformBind(ob, + smd_orig, + smd, + vertexCos, + verts_num, + target_polys_num, + target_verts_num, + target, + mesh)) { smd->flags &= ~MOD_SDEF_BIND; } /* Early abort, this is binding 'call', no need to perform whole evaluation. */ return; } - /* Poly count checks */ + /* Geometry count on the deforming mesh. */ if (smd->mesh_verts_num != verts_num) { BKE_modifier_set_error( ob, md, "Vertices changed from %u to %u", smd->mesh_verts_num, verts_num); return; } - if (smd->polys_num != tpolys_num) { + + /* Geometry count on the target mesh. */ + if (smd->target_polys_num != target_polys_num && smd->target_verts_num == 0) { + /* Change in the number of polygons does not really imply change in the vertex count, but + * this is how the modifier worked before the vertex count was known. Follow the legacy + * logic without requirement to re-bind the mesh. */ BKE_modifier_set_error( - ob, md, "Target polygons changed from %u to %u", smd->polys_num, tpolys_num); + ob, md, "Target polygons changed from %u to %u", smd->target_polys_num, target_polys_num); return; } + if (smd->target_verts_num != 0 && smd->target_verts_num != target_verts_num) { + if (smd->target_verts_num > target_verts_num) { + /* Number of vertices on the target did reduce. There is no usable recovery from this. */ + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u", + smd->target_verts_num, + target_verts_num); + return; + } + + /* Assume the increase in the vertex count means that the "new" vertices in the target mesh are + * added after the original ones. This covers typical case when target was at the subdivision + * level 0 and then subdivision was increased (i.e. for the render purposes). */ + + BKE_modifier_set_error(ob, + md, + "Target vertices changed from %u to %u, continuing anyway", + smd->target_verts_num, + target_verts_num); + + /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but + * it is not currently possible to request a subset of coordinates: the API expects that the + * caller needs coordinates of all vertices and asserts for it. */ + } /* Early out if modifier would not affect input at all - still *after* the sanity checks * (and potential binding) above. */ @@ -1507,7 +1546,7 @@ static void surfacedeformModifier_do(ModifierData *md, /* Actual vertex location update starts here */ SDefDeformData data = { .bind_verts = smd->verts, - .targetCos = MEM_malloc_arrayN(tverts_num, sizeof(float[3]), "SDefTargetVertArray"), + .targetCos = MEM_malloc_arrayN(target_verts_num, sizeof(float[3]), "SDefTargetVertArray"), .vertexCos = vertexCos, .dvert = dvert, .defgrp_index = defgrp_index, @@ -1516,7 +1555,8 @@ static void surfacedeformModifier_do(ModifierData *md, }; if (data.targetCos != NULL) { - BKE_mesh_wrapper_vert_coords_copy_with_mat4(target, data.targetCos, tverts_num, smd->mat); + BKE_mesh_wrapper_vert_coords_copy_with_mat4( + target, data.targetCos, target_verts_num, smd->mat); TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -1629,27 +1669,41 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_SurfaceDeform, panel_draw); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *id_owner, const ModifierData *md) { - const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md; + SurfaceDeformModifierData smd = *(const SurfaceDeformModifierData *)md; + + if (ID_IS_OVERRIDE_LIBRARY(id_owner)) { + BLI_assert(!ID_IS_LINKED(id_owner)); + const bool is_local = (md->flag & eModifierFlag_OverrideLibrary_Local) != 0; + if (!is_local) { + /* Modifier coming from linked data cannot be bound from an override, so we can remove all + * binding data, can save a significant amount of memory. */ + smd.bind_verts_num = 0; + smd.verts = NULL; + } + } - BLO_write_struct_array(writer, SDefVert, smd->bind_verts_num, smd->verts); + BLO_write_struct_at_address(writer, SurfaceDeformModifierData, md, &smd); - if (smd->verts) { - for (int i = 0; i < smd->bind_verts_num; i++) { - BLO_write_struct_array(writer, SDefBind, smd->verts[i].binds_num, smd->verts[i].binds); + if (smd.verts != NULL) { + SDefVert *bind_verts = smd.verts; + BLO_write_struct_array(writer, SDefVert, smd.bind_verts_num, bind_verts); - if (smd->verts[i].binds) { - for (int j = 0; j < smd->verts[i].binds_num; j++) { + for (int i = 0; i < smd.bind_verts_num; i++) { + BLO_write_struct_array(writer, SDefBind, bind_verts[i].binds_num, bind_verts[i].binds); + + if (bind_verts[i].binds) { + for (int j = 0; j < bind_verts[i].binds_num; j++) { BLO_write_uint32_array( - writer, smd->verts[i].binds[j].verts_num, smd->verts[i].binds[j].vert_inds); + writer, bind_verts[i].binds[j].verts_num, bind_verts[i].binds[j].vert_inds); - if (ELEM(smd->verts[i].binds[j].mode, MOD_SDEF_MODE_CENTROID, MOD_SDEF_MODE_LOOPTRI)) { - BLO_write_float3_array(writer, 1, smd->verts[i].binds[j].vert_weights); + if (ELEM(bind_verts[i].binds[j].mode, MOD_SDEF_MODE_CENTROID, MOD_SDEF_MODE_LOOPTRI)) { + BLO_write_float3_array(writer, 1, bind_verts[i].binds[j].vert_weights); } else { BLO_write_float_array( - writer, smd->verts[i].binds[j].verts_num, smd->verts[i].binds[j].vert_weights); + writer, bind_verts[i].binds[j].verts_num, bind_verts[i].binds[j].vert_weights); } } } diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index a58e8e23147..575182a846b 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -100,10 +100,9 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd, BLI_bitmap *done = BLI_BITMAP_NEW(verts_num, __func__); const int polys_num = mesh->totpoly; char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - MLoopUV *mloop_uv; CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, dmd->uvlayer_name, uvname); - mloop_uv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); + const MLoopUV *mloop_uv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); /* verts are given the UV from the first face that uses them */ for (i = 0, mp = mpoly; i < polys_num; i++, mp++) { diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index aef254b1103..b3b75898557 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -11,6 +11,10 @@ #include "DEG_depsgraph_build.h" +#ifdef __cplusplus +extern "C" { +#endif + struct MDeformVert; struct Mesh; struct ModifierData; @@ -51,3 +55,7 @@ void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, struct Object *object, const char *bonename, const char *description); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 79972d1911d..402d7b2c99e 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -489,10 +489,12 @@ static void panelRegister(ARegionType *region_type) region_type, "texture", "Texture", NULL, texture_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WarpModifierData *wmd = (const WarpModifierData *)md; + BLO_write_struct(writer, WarpModifierData, wmd); + if (wmd->curfalloff) { BKE_curvemapping_blend_write(writer, wmd->curfalloff); } diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index c79dbdb0b1a..d436acb8ad5 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -82,7 +82,7 @@ typedef struct WeightedNormalData { MPoly *mpoly; const float (*polynors)[3]; - int *poly_strength; + const int *poly_strength; MDeformVert *dvert; const int defgrp_index; @@ -195,7 +195,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, MPoly *mpoly = wn_data->mpoly; const float(*polynors)[3] = wn_data->polynors; - int *poly_strength = wn_data->poly_strength; + const int *poly_strength = wn_data->poly_strength; MDeformVert *dvert = wn_data->dvert; @@ -326,7 +326,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, } break; default: - BLI_assert(0); + BLI_assert_unreachable(); } /* Validate computed weighted normals. */ @@ -603,15 +603,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } const float split_angle = mesh->smoothresh; - short(*clnors)[2]; - CustomData *ldata = &result->ldata; - clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); + short(*clnors)[2] = CustomData_get_layer(&result->ldata, CD_CUSTOMLOOPNORMAL); /* Keep info whether we had clnors, * it helps when generating clnor spaces and default normals. */ const bool has_clnors = clnors != NULL; if (!clnors) { - clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num); + clnors = CustomData_add_layer(&result->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num); } MDeformVert *dvert; diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index b251825cd95..65393370268 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -96,7 +96,7 @@ void weightvg_do_map( BLI_assert(do_invert); break; default: - BLI_assert(0); + BLI_assert_unreachable(); } new_w[i] = do_invert ? 1.0f - fac : fac; @@ -206,8 +206,6 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, MEM_freeN(tex_co); } else if ((ref_didx = BKE_id_defgroup_name_index(&mesh->id, defgrp_name)) != -1) { - MDeformVert *dvert = NULL; - /* Check whether we want to set vgroup weights from a constant weight factor or a vertex * group. */ @@ -215,7 +213,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, /* Proceed only if vgroup is valid, else use constant factor. */ /* Get actual dverts (ie vertex group data). */ - dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); /* Proceed only if vgroup is valid, else assume factor = O. */ if (dvert == NULL) { return; diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 2c733542e51..e1b43157adb 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -375,10 +375,12 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; + BLO_write_struct(writer, WeightVGEditModifierData, wmd); + if (wmd->cmap_curve) { BKE_curvemapping_blend_write(writer, wmd->cmap_curve); } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 43a90b2a4ac..1bea5b93c97 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -715,10 +715,12 @@ static void panelRegister(ARegionType *region_type) region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); } -static void blendWrite(BlendWriter *writer, const ModifierData *md) +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) { const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; + BLO_write_struct(writer, WeightVGProximityModifierData, wmd); + if (wmd->cmap_curve) { BKE_curvemapping_blend_write(writer, wmd->cmap_curve); } diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 3d3450d9252..5d782674f16 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -126,6 +126,8 @@ void register_node_type_cmp_planetrackdeform(void); void register_node_type_cmp_cornerpin(void); void register_node_type_cmp_separate_xyz(void); void register_node_type_cmp_combine_xyz(void); +void register_node_type_cmp_separate_color(void); +void register_node_type_cmp_combine_color(void); void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node); void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index cde4b67e120..ad96fba1929 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -8,6 +8,7 @@ extern "C" { void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); +void register_node_type_fn_combine_color(void); void register_node_type_fn_compare(void); void register_node_type_fn_float_to_int(void); void register_node_type_fn_input_bool(void); @@ -19,6 +20,7 @@ void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); void register_node_type_fn_replace_string(void); void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_separate_color(void); void register_node_type_fn_slice_string(void); void register_node_type_fn_string_length(void); void register_node_type_fn_value_to_string(void); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 43792a2d90a..2917861f084 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -104,16 +104,16 @@ class GeometryValueLog : public ValueLog { public: struct MeshInfo { - int tot_verts, tot_edges, tot_faces; + int verts_num, edges_num, faces_num; }; struct CurveInfo { - int tot_splines; + int splines_num; }; struct PointCloudInfo { - int tot_points; + int points_num; }; struct InstancesInfo { - int tot_instances; + int instances_num; }; std::optional<MeshInfo> mesh_info; diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 61d1d11d859..257aa5f4110 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -41,6 +41,7 @@ #include "BLI_vector.hh" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "DNA_node_types.h" @@ -597,7 +598,7 @@ inline bNodeType *NodeRef::typeinfo() const inline const NodeDeclaration *NodeRef::declaration() const { nodeDeclarationEnsure(this->tree().btree(), bnode_); - return bnode_->declaration; + return bnode_->runtime->declaration; } inline int NodeRef::id() const diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 4996f12e27d..1d1310360b8 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -43,6 +43,8 @@ void register_node_type_sh_vect_math(void); void register_node_type_sh_squeeze(void); void register_node_type_sh_dynamic(void); void register_node_type_sh_invert(void); +void register_node_type_sh_sepcolor(void); +void register_node_type_sh_combcolor(void); void register_node_type_sh_seprgb(void); void register_node_type_sh_combrgb(void); void register_node_type_sh_sephsv(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5818fa631e7..e0a4d241b3b 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -42,9 +42,9 @@ DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" ) DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" ) DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) -DefNode(ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) -DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) -DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) +DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" ) +DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "" ) +DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" ) @@ -106,8 +106,8 @@ DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" ) DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "" ) DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" ) -DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) -DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) +DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) +DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" ) DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" ) DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" ) @@ -120,6 +120,8 @@ DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" ) DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" ) +DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -137,8 +139,8 @@ DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VA DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" ) DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time Curve", "" ) DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" ) -DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPRGBA_LEGACY, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPHSVA_LEGACY, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) DefNode(CompositorNode, CMP_NODE_SETALPHA, def_cmp_set_alpha, "SETALPHA", SetAlpha, "Set Alpha", "" ) DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" ) DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" ) @@ -149,16 +151,16 @@ DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" ) DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" ) DefNode(CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" ) -DefNode(CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBRGBA_LEGACY,0, "COMBRGBA", CombRGBA, "Combine RGBA", "" ) DefNode(CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" ) DefNode(CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" ) DefNode(CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" ) DefNode(CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" ) DefNode(CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) -DefNode(CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) -DefNode(CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYCCA_LEGACY, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYCCA_LEGACY,def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" ) +DefNode(CompositorNode, CMP_NODE_SEPYUVA_LEGACY, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBYUVA_LEGACY,0, "COMBYUVA", CombYUVA, "Combine YUVA", "" ) DefNode(CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" ) DefNode(CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" ) DefNode(CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" ) @@ -170,7 +172,7 @@ DefNode(CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MAS DefNode(CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" ) DefNode(CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" ) DefNode(CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" ) -DefNode(CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) +DefNode(CompositorNode, CMP_NODE_COMBHSVA_LEGACY,0, "COMBHSVA", CombHSVA, "Combine HSVA", "" ) DefNode(CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" ) DefNode(CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" ) DefNode(CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" ) @@ -214,10 +216,12 @@ DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOIS DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" ) DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) -DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" ) -DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) +DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space", "" ) +DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBINE_XYZ", CombineXYZ, "Combine XYZ", "" ) DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" ) +DefNode(CompositorNode, CMP_NODE_SEPARATE_COLOR, def_cmp_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) +DefNode(CompositorNode, CMP_NODE_COMBINE_COLOR, def_cmp_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -230,18 +234,20 @@ DefNode(TextureNode, TEX_NODE_VALTORGB, def_colorramp, "VALTOR DefNode(TextureNode, TEX_NODE_IMAGE, def_tex_image, "IMAGE", Image, "Image", "" ) DefNode(TextureNode, TEX_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" ) DefNode(TextureNode, TEX_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" ) -DefNode(TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) +DefNode(TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value", "" ) DefNode(TextureNode, TEX_NODE_CURVE_TIME, def_time, "CURVE_TIME", CurveTime, "Curve Time", "" ) DefNode(TextureNode, TEX_NODE_ROTATE, 0, "ROTATE", Rotate, "Rotate", "" ) DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "" ) DefNode(TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" ) DefNode(TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" ) DefNode(TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" ) -DefNode(TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" ) -DefNode(TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) +DefNode(TextureNode, TEX_NODE_COMPOSE_LEGACY, 0, "COMPOSE", Compose, "Combine RGBA", "" ) +DefNode(TextureNode, TEX_NODE_DECOMPOSE_LEGACY,0, "DECOMPOSE", Decompose, "Separate RGBA", "" ) DefNode(TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" ) DefNode(TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" ) DefNode(TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" ) +DefNode(TextureNode, TEX_NODE_COMBINE_COLOR, def_tex_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" ) +DefNode(TextureNode, TEX_NODE_SEPARATE_COLOR, def_tex_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" ) /* procedural textures */ DefNode(TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" ) @@ -256,6 +262,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") +DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "") DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") @@ -267,6 +274,7 @@ DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, def_fn_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "") DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h index 0f07b17f165..9a2dc705c0d 100644 --- a/source/blender/nodes/NOD_texture.h +++ b/source/blender/nodes/NOD_texture.h @@ -46,6 +46,8 @@ void register_node_type_tex_at(void); void register_node_type_tex_compose(void); void register_node_type_tex_decompose(void); +void register_node_type_tex_combine_color(void); +void register_node_type_tex_separate_color(void); void register_node_type_tex_proc_voronoi(void); void register_node_type_tex_proc_blend(void); diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 57f76f20ac6..c0100d77889 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC nodes/node_composite_rotate.cc nodes/node_composite_scale.cc nodes/node_composite_scene_time.cc + nodes/node_composite_sepcomb_color.cc nodes/node_composite_sepcomb_hsva.cc nodes/node_composite_sepcomb_rgba.cc nodes/node_composite_sepcomb_xyz.cc diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc new file mode 100644 index 00000000000..b253656a628 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_composite_util.hh" + +static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeCMPCombSepColor *data = MEM_cnew<NodeCMPCombSepColor>(__func__); + data->mode = CMP_NODE_COMBSEP_COLOR_RGB; + data->ycc_mode = BLI_YCC_ITU_BT709; + node->storage = data; +} + +static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case CMP_NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + case CMP_NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case CMP_NODE_COMBSEP_COLOR_YCC: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "Cb"); + node_sock_label(sock3, "Cr"); + break; + case CMP_NODE_COMBSEP_COLOR_YUV: + node_sock_label(sock1, "Y"); + node_sock_label(sock2, "U"); + node_sock_label(sock3, "V"); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_composite_separate_color_cc { + +static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_separate_color_cc + +void register_node_type_cmp_separate_color() +{ + namespace file_ns = blender::nodes::node_composite_separate_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_separate_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_separate_color_update); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_composite_combine_color_cc { + +static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Image")); +} + +static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)node->storage; + node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode); +} + +} // namespace blender::nodes::node_composite_combine_color_cc + +void register_node_type_cmp_combine_color() +{ + namespace file_ns = blender::nodes::node_composite_combine_color_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combine_color_declare; + node_type_init(&ntype, node_cmp_combsep_color_init); + node_type_storage( + &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::cmp_node_combine_color_update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index 349c27d876d..a0d2485ea5a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sephsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; nodeRegisterType(&ntype); } @@ -54,7 +54,7 @@ void register_node_type_cmp_combhsva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index c46603be847..ae46681b0f4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -27,7 +27,7 @@ void register_node_type_cmp_seprgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; nodeRegisterType(&ntype); @@ -54,7 +54,7 @@ void register_node_type_cmp_combrgba() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 9b5c153fddf..a3c40b61e64 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -33,7 +33,7 @@ void register_node_type_cmp_sepycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); @@ -66,7 +66,7 @@ void register_node_type_cmp_combycca() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index e458c9cfb7e..7fdece5904d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -28,7 +28,7 @@ void register_node_type_cmp_sepyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; nodeRegisterType(&ntype); @@ -55,7 +55,7 @@ void register_node_type_cmp_combyuva() static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER); + cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt index 6ccc4c7bf5c..d03f1cb63ff 100644 --- a/source/blender/nodes/function/CMakeLists.txt +++ b/source/blender/nodes/function/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC set(SRC nodes/node_fn_align_euler_to_vector.cc nodes/node_fn_boolean_math.cc + nodes/node_fn_combine_color.cc nodes/node_fn_compare.cc nodes/node_fn_float_to_int.cc nodes/node_fn_input_bool.cc @@ -31,6 +32,7 @@ set(SRC nodes/node_fn_random_value.cc nodes/node_fn_replace_string.cc nodes/node_fn_rotate_euler.cc + nodes/node_fn_separate_color.cc nodes/node_fn_slice_string.cc nodes/node_fn_string_length.cc nodes/node_fn_value_to_string.cc diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc new file mode 100644 index 00000000000..c5fd3ce38a1 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_combine_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +}; + +static void fn_node_combine_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + const NodeCombSepColor &storage = node_storage(bnode); + + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> rgba_fn{ + "RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsva_fn{ + "HSV", [](float h, float s, float v, float a) { + ColorGeometry4f r_color; + hsv_to_rgb(h, s, v, &r_color.r, &r_color.g, &r_color.b); + r_color.a = a; + return r_color; + }}; + static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsla_fn{ + "HSL", [](float h, float s, float l, float a) { + ColorGeometry4f color; + hsl_to_rgb(h, s, l, &color.r, &color.g, &color.b); + color.a = a; + return color; + }}; + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: + return &rgba_fn; + case NODE_COMBSEP_COLOR_HSV: + return &hsva_fn; + case NODE_COMBSEP_COLOR_HSL: + return &hsla_fn; + } + + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_combine_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_combine_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_combine_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_combine_color_update); + node_type_init(&ntype, blender::nodes::fn_node_combine_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_combine_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_combine_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_separate_color.cc b/source/blender/nodes/function/nodes/node_fn_separate_color.cc new file mode 100644 index 00000000000..1701dfdc6fa --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_separate_color.cc @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void fn_node_separate_color_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); + b.add_output<decl::Float>(N_("Alpha")); +}; + +static void fn_node_separate_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void fn_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static void fn_node_separate_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +class SeparateRGBAFunction : public fn::MultiFunction { + public: + SeparateRGBAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Red"); + signature.single_output<float>("Green"); + signature.single_output<float>("Blue"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> red = params.uninitialized_single_output<float>(1, "Red"); + MutableSpan<float> green = params.uninitialized_single_output<float>(2, "Green"); + MutableSpan<float> blue = params.uninitialized_single_output<float>(3, "Blue"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + red[i] = colors[i].r; + green[i] = colors[i].g; + blue[i] = colors[i].b; + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSVAFunction : public fn::MultiFunction { + public: + SeparateHSVAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Value"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> value = params.uninitialized_single_output<float>(3, "Value"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsv(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &value[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +class SeparateHSLAFunction : public fn::MultiFunction { + public: + SeparateHSLAFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Separate Color"}; + signature.single_input<ColorGeometry4f>("Color"); + signature.single_output<float>("Hue"); + signature.single_output<float>("Saturation"); + signature.single_output<float>("Lightness"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0, + "Color"); + MutableSpan<float> hue = params.uninitialized_single_output<float>(1, "Hue"); + MutableSpan<float> saturation = params.uninitialized_single_output<float>(2, "Saturation"); + MutableSpan<float> lightness = params.uninitialized_single_output<float>(3, "Lightness"); + MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha"); + + for (int64_t i : mask) { + rgb_to_hsl(colors[i].r, colors[i].g, colors[i].b, &hue[i], &saturation[i], &lightness[i]); + } + + if (!alpha.is_empty()) { + for (int64_t i : mask) { + alpha[i] = colors[i].a; + } + } + } +}; + +static void fn_node_separate_color_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeCombSepColor &storage = node_storage(builder.node()); + + switch (storage.mode) { + case NODE_COMBSEP_COLOR_RGB: { + static SeparateRGBAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSV: { + static SeparateHSVAFunction fn; + builder.set_matching_fn(fn); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + static SeparateHSLAFunction fn; + builder.set_matching_fn(fn); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +} // namespace blender::nodes + +void register_node_type_fn_separate_color(void) +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = blender::nodes::fn_node_separate_color_declare; + node_type_update(&ntype, blender::nodes::fn_node_separate_color_update); + node_type_init(&ntype, blender::nodes::fn_node_separate_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_separate_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_separate_color_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index e081e007c81..38e914b9a9f 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -110,7 +110,7 @@ void register_node_tree_type_geo() tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = ICON_NODETREE; + tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7af3159bbf8..8f20da66c3b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -57,8 +57,6 @@ Mesh *create_cylinder_or_cone_mesh(float radius_top, GeometryNodeMeshCircleFillType fill_type, ConeAttributeOutputs &attribute_outputs); -Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); - /** * Copies the point domain attributes from `in_component` that are in the mask to `out_component`. */ @@ -81,14 +79,4 @@ void separate_geometry(GeometrySet &geometry_set, std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); -class SplineLengthFieldInput final : public GeometryFieldInput { - public: - SplineLengthFieldInput(); - GVArray get_varray_for_context(const GeometryComponent &component, - AttributeDomain domain, - IndexMask mask) const final; - uint64_t hash() const override; - bool is_equal_to(const fn::FieldNode &other) const override; -}; - } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index ea26eec0c15..b29831ceeb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -217,16 +217,16 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); const VArray<T> &values = evaluator.get_evaluated<T>(0); const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); if (group_indices.is_single()) { T accumulation = T(); @@ -303,9 +303,9 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { IndexMask UNUSED(mask)) const final { const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const int domain_num = component.attribute_domain_num(field_context.domain()); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -317,10 +317,10 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { for (const int i : values.index_range()) { accumulation = values[i] + accumulation; } - return VArray<T>::ForSingle(accumulation, domain_size); + return VArray<T>::ForSingle(accumulation, domain_num); } - Array<T> accumulations_out(domain_size); + Array<T> accumulations_out(domain_num); Map<int, T> accumulations; for (const int i : values.index_range()) { T &value = accumulations.lookup_or_add_default(group_indices[i]); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 45a6aabeb03..16967d32673 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -112,8 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); OutputAttribute output_attribute = component.attribute_try_get_for_output_only( @@ -126,30 +126,66 @@ static void try_capture_field_on_geometry(GeometryComponent &component, output_attribute.save(); } +static StringRefNull identifier_suffix(CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return "_001"; + case CD_PROP_INT32: + return "_004"; + case CD_PROP_COLOR: + return "_002"; + case CD_PROP_BOOL: + return "_003"; + case CD_PROP_FLOAT3: + return ""; + default: + BLI_assert_unreachable(); + return ""; + } +} + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + if (!params.output_is_required("Geometry")) { + params.error_message_add( + NodeWarningType::Info, + TIP_("The attribute output can not be used without the geometry output")); + params.set_default_remaining_outputs(); + return; + } + const NodeGeometryAttributeCapture &storage = node_storage(params.node()); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + const std::string output_identifier = "Attribute" + identifier_suffix(data_type); + + if (!params.output_is_required(output_identifier)) { + params.set_output("Geometry", geometry_set); + return; + } + + const std::string input_identifier = "Value" + identifier_suffix(data_type); GField field; + switch (data_type) { case CD_PROP_FLOAT: - field = params.get_input<Field<float>>("Value_001"); + field = params.get_input<Field<float>>(input_identifier); break; case CD_PROP_FLOAT3: - field = params.get_input<Field<float3>>("Value"); + field = params.get_input<Field<float3>>(input_identifier); break; case CD_PROP_COLOR: - field = params.get_input<Field<ColorGeometry4f>>("Value_002"); + field = params.get_input<Field<ColorGeometry4f>>(input_identifier); break; case CD_PROP_BOOL: - field = params.get_input<Field<bool>>("Value_003"); + field = params.get_input<Field<bool>>(input_identifier); break; case CD_PROP_INT32: - field = params.get_input<Field<int>>("Value_004"); + field = params.get_input<Field<int>>(input_identifier); break; default: break; @@ -185,23 +221,23 @@ static void node_geo_exec(GeoNodeExecParams params) switch (data_type) { case CD_PROP_FLOAT: { - params.set_output("Attribute_001", Field<float>(output_field)); + params.set_output(output_identifier, Field<float>(output_field)); break; } case CD_PROP_FLOAT3: { - params.set_output("Attribute", Field<float3>(output_field)); + params.set_output(output_identifier, Field<float3>(output_field)); break; } case CD_PROP_COLOR: { - params.set_output("Attribute_002", Field<ColorGeometry4f>(output_field)); + params.set_output(output_identifier, Field<ColorGeometry4f>(output_field)); break; } case CD_PROP_BOOL: { - params.set_output("Attribute_003", Field<bool>(output_field)); + params.set_output(output_identifier, Field<bool>(output_field)); break; } case CD_PROP_INT32: { - params.set_output("Attribute_004", Field<int>(output_field)); + params.set_output(output_identifier, Field<int>(output_field)); break; } default: 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 b3fe9d160b3..8ab0eb678e7 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 @@ -72,11 +72,11 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_MESH: { if (geometry_set.has_mesh()) { const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); - params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE)); - params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE)); + params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE)); params.set_output("Face Corner Count", - component->attribute_domain_size(ATTR_DOMAIN_CORNER)); + component->attribute_domain_num(ATTR_DOMAIN_CORNER)); } else { params.set_default_remaining_outputs(); @@ -86,8 +86,8 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_COMPONENT_TYPE_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)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); + params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE)); } else { params.set_default_remaining_outputs(); @@ -98,7 +98,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_pointcloud()) { const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -109,8 +109,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { const InstancesComponent *component = geometry_set.get_component_for_read<InstancesComponent>(); - params.set_output("Instance Count", - component->attribute_domain_size(ATTR_DOMAIN_INSTANCE)); + params.set_output("Instance Count", component->attribute_domain_num(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1153f18ffd4..c7f65a68d60 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -197,9 +197,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); @@ -275,9 +275,9 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; - const int domain_size = component->attribute_domain_size(domain); + const int domain_num = component->attribute_domain_num(domain); - fn::FieldEvaluator data_evaluator{field_context, domain_size}; + fn::FieldEvaluator data_evaluator{field_context, domain_num}; data_evaluator.add(input_field); data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 558129fb384..00b10cc8a2f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "GEO_mesh_primitive_cuboid.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_bounding_box_cc { @@ -53,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) else { const float3 scale = sub_max - sub_min; const float3 center = sub_min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map"); transform_mesh(*mesh, center, float3(0), float3(1)); sub_geometry.replace_mesh(mesh); sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); 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 09d0f13c50d..31f706c497c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -138,14 +138,14 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) { int span_count = 0; int count = 0; - int total_size = 0; + int total_num = 0; Span<float3> positions_span; if (geometry_set.has_mesh()) { count++; const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT); + total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT); } if (geometry_set.has_pointcloud()) { @@ -155,7 +155,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) geometry_set.get_component_for_read<PointCloudComponent>(); VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - total_size += varray.size(); + total_num += varray.size(); positions_span = varray.get_internal_span(); } @@ -165,7 +165,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); positions_span = curves.evaluated_positions(); - total_size += positions_span.size(); + total_num += positions_span.size(); } if (count == 0) { @@ -178,7 +178,7 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(nullptr, positions_span); } - Array<float3> positions(total_size); + Array<float3> positions(total_num); int offset = 0; if (geometry_set.has_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 95ea978541c..fb8a488ddae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -106,13 +106,13 @@ static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const /* Calculate the direction vectors from each vertex to their previous vertex. */ static Array<float3> calculate_directions(const Span<float3> positions) { - const int size = positions.size(); - Array<float3> directions(size); + const int num = positions.size(); + Array<float3> directions(num); - for (const int i : IndexRange(size - 1)) { + for (const int i : IndexRange(num - 1)) { directions[i] = math::normalize(positions[i + 1] - positions[i]); } - directions[size - 1] = math::normalize(positions[0] - positions[size - 1]); + directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); return directions; } @@ -120,11 +120,11 @@ static Array<float3> calculate_directions(const Span<float3> positions) /* Calculate the axes around which the fillet is built. */ static Array<float3> calculate_axes(const Span<float3> directions) { - const int size = directions.size(); - Array<float3> axes(size); + const int num = directions.size(); + Array<float3> axes(num); - axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0])); - for (const int i : IndexRange(1, size - 1)) { + axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); + for (const int i : IndexRange(1, num - 1)) { axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); } @@ -134,11 +134,11 @@ static Array<float3> calculate_axes(const Span<float3> directions) /* Calculate the angle of the arc formed by the fillet. */ static Array<float> calculate_angles(const Span<float3> directions) { - const int size = directions.size(); - Array<float> angles(size); + const int num = directions.size(); + Array<float> angles(num); - angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); - for (const int i : IndexRange(1, size - 1)) { + angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); + for (const int i : IndexRange(1, num - 1)) { angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); } @@ -147,18 +147,18 @@ static Array<float> calculate_angles(const Span<float3> directions) /* Calculate the segment count in each filleted arc. */ static Array<int> calculate_counts(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset, const bool cyclic) { - Array<int> counts(size, 1); + Array<int> counts(num, 1); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { counts[i] = fillet_param.counts[spline_offset + i]; } } if (!cyclic) { - counts[0] = counts[size - 1] = 0; + counts[0] = counts[num - 1] = 0; } return counts; @@ -166,17 +166,17 @@ static Array<int> calculate_counts(const FilletParam &fillet_param, /* Calculate the radii for the vertices to be filleted. */ static Array<float> calculate_radii(const FilletParam &fillet_param, - const int size, + const int num, const int spline_offset) { - Array<float> radii(size, 0.0f); + Array<float> radii(num, 0.0f); if (fillet_param.limit_radius) { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); } } else { - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = fillet_param.radii[spline_offset + i]; } } @@ -207,15 +207,15 @@ static FilletData calculate_fillet_data(const Spline &spline, MutableSpan<int> point_counts, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); FilletData fd; fd.directions = calculate_directions(spline.positions()); fd.positions = spline.positions(); fd.axes = calculate_axes(fd.directions); fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, size, spline_offset); + fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, num, spline_offset); added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); @@ -229,19 +229,19 @@ static void limit_radii(FilletData &fd, const bool cyclic) Span<float> angles(fd.angles); Span<float3> positions(fd.positions); - const int size = radii.size(); - const int fillet_count = cyclic ? size : size - 2; + const int num = radii.size(); + const int fillet_count = cyclic ? num : num - 2; const int start = cyclic ? 0 : 1; - Array<float> max_radii(size, FLT_MAX); + Array<float> max_radii(num, FLT_MAX); if (cyclic) { /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[size - 1]); + const float len_prev = math::distance(positions[0], positions[num - 1]); const float len_next = math::distance(positions[0], positions[1]); /* Calculate tangent lengths of fillets in control points. */ const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); float factor_prev = 1.0f, factor_next = 1.0f; @@ -255,12 +255,12 @@ static void limit_radii(FilletData &fd, const bool cyclic) /* Scale max radii by calculated factors. */ max_radii[0] = radii[0] * std::min(factor_next, factor_prev); max_radii[1] = radii[1] * factor_next; - max_radii[size - 1] = radii[size - 1] * factor_prev; + max_radii[num - 1] = radii[num - 1] * factor_prev; } /* Initialize max_radii to largest possible radii. */ float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, size - 2)) { + for (const int i : IndexRange(1, num - 2)) { const float temp_dist = math::distance(positions[i], positions[i + 1]); max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); prev_dist = temp_dist; @@ -282,7 +282,7 @@ static void limit_radii(FilletData &fd, const bool cyclic) } /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(size)) { + for (const int i : IndexRange(num)) { radii[i] = std::min(radii[i], max_radii[i]); } } @@ -358,10 +358,10 @@ static void update_bezier_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -385,7 +385,7 @@ static void update_bezier_positions(const FilletData &fd, /* Position the end points of the arc and their handles. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -442,10 +442,10 @@ static void update_poly_positions(const FilletData &fd, Span<float3> positions(fd.positions); Span<float3> directions(fd.directions); - const int size = radii.size(); + const int num = radii.size(); int i_dst = 0; - for (const int i_src : IndexRange(size)) { + for (const int i_src : IndexRange(num)) { const int count = point_counts[i_src]; /* Skip if the point count for the vertex is 1. */ @@ -460,7 +460,7 @@ static void update_poly_positions(const FilletData &fd, /* Position the end points of the arc. */ const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; const float3 next_dir = directions[i_src]; dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; @@ -487,15 +487,15 @@ static SplinePtr fillet_spline(const Spline &spline, const FilletParam &fillet_param, const int spline_offset) { - const int size = spline.size(); + const int num = spline.size(); const bool cyclic = spline.is_cyclic(); - if (size < 3) { + if (num < 3) { return spline.copy(); } /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array<int> point_counts(size, 1); + Array<int> point_counts(num, 1); int added_count = 0; /* Update point_counts array and added_count. */ @@ -505,7 +505,7 @@ static SplinePtr fillet_spline(const Spline &spline, limit_radii(fd, cyclic); } - const int total_points = added_count + size; + const int total_points = added_count + num; const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points); SplinePtr dst_spline_ptr = spline.copy_only_settings(); (*dst_spline_ptr).resize(total_points); @@ -581,8 +581,8 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_num}; field_evaluator.add(radius_field); 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 e0348f27e51..78a132064ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -1,12 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_array.hh" -#include "BLI_index_mask_ops.hh" -#include "BLI_length_parameterize.hh" -#include "BLI_task.hh" -#include "BLI_timeit.hh" +#include "GEO_resample_curves.hh" -#include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "UI_interface.h" @@ -56,556 +51,6 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -/** Returns the number of evaluated points in each curve. Used to deselect curves with none. */ -class EvaluatedCountFieldInput final : public GeometryFieldInput { - public: - EvaluatedCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Evaluated Point Count") - { - category_ = Category::Generated; - } - - GVArray get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const final - { - if (component.type() == GEO_COMPONENT_TYPE_CURVE && domain == ATTR_DOMAIN_CURVE && - !component.is_empty()) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - curves.ensure_evaluated_offsets(); - return VArray<int>::ForFunc(curves.curves_num(), [&](const int64_t index) -> int { - return curves.evaluated_points_for_curve(index).size(); - }); - } - return {}; - } - - uint64_t hash() const override - { - /* Some random constant hash. */ - return 234905872379865; - } - - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const EvaluatedCountFieldInput *>(&other) != nullptr; - } -}; - -/** - * Return true if the attribute should be copied/interpolated to the result curves. - * Don't output attributes that correspond to curve types that have no curves in the result. - */ -static bool interpolate_attribute_to_curves(const AttributeIDRef &attribute_id, - const std::array<int, CURVE_TYPES_NUM> &type_counts) -{ - if (!attribute_id.is_named()) { - return true; - } - if (ELEM(attribute_id.name(), - "handle_type_left", - "handle_type_right", - "handle_left", - "handle_right")) { - return type_counts[CURVE_TYPE_BEZIER] != 0; - } - if (ELEM(attribute_id.name(), "nurbs_weight")) { - return type_counts[CURVE_TYPE_NURBS] != 0; - } - return true; -} - -/** - * Return true if the attribute should be copied to poly curves. - */ -static bool interpolate_attribute_to_poly_curve(const AttributeIDRef &attribute_id) -{ - static const Set<StringRef> no_interpolation{{ - "handle_type_left", - "handle_type_right", - "handle_position_right", - "handle_position_left", - "nurbs_weight", - }}; - return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name())); -} - -/** - * Retrieve spans from source and result attributes. - */ -static void retrieve_attribute_spans(const Span<AttributeIDRef> ids, - const CurveComponent &src_component, - CurveComponent &dst_component, - Vector<GSpan> &src, - Vector<GMutableSpan> &dst, - Vector<OutputAttribute> &dst_attributes) -{ - for (const int i : ids.index_range()) { - GVArray src_attribute = src_component.attribute_try_get_for_read(ids[i], ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - src.append(src_attribute.get_internal_span()); - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type()); - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - ids[i], ATTR_DOMAIN_POINT, data_type); - dst.append(dst_attribute.as_span()); - dst_attributes.append(std::move(dst_attribute)); - } -} - -struct AttributesForInterpolation : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<OutputAttribute> dst_attributes; - - Vector<GSpan> src_no_interpolation; - Vector<GMutableSpan> dst_no_interpolation; -}; - -/** - * Gather a set of all generic attribute IDs to copy to the result curves. - */ -static void gather_point_attributes_to_interpolate(const CurveComponent &src_component, - CurveComponent &dst_component, - AttributesForInterpolation &result) -{ - const Curves &dst_curves_id = *dst_component.get_for_read(); - const bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id.geometry); - - VectorSet<AttributeIDRef> ids; - VectorSet<AttributeIDRef> ids_no_interpolation; - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - return true; - } - if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { - return true; - } - if (interpolate_attribute_to_poly_curve(id)) { - ids.add_new(id); - } - else { - ids_no_interpolation.add_new(id); - } - return true; - }); - - /* Position is handled differently since it has non-generic interpolation for Bezier - * curves and because the evaluated positions are cached for each evaluated point. */ - ids.remove_contained("position"); - - retrieve_attribute_spans( - ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - - /* Attributes that aren't interpolated like Bezier handles still have to be be copied - * to the result when there are any unselected curves of the corresponding type. */ - retrieve_attribute_spans(ids_no_interpolation, - src_component, - dst_component, - result.src_no_interpolation, - result.dst_no_interpolation, - result.dst_attributes); -} - -/** - * Copy the provided point attribute values between all curves in the #curve_ranges index - * ranges, assuming that all curves are the same size in #src_curves and #dst_curves. - */ -template<typename T> -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> curve_ranges, - const Span<T> src, - const MutableSpan<T> dst) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) { - for (const IndexRange range : curve_ranges.slice(range)) { - const IndexRange src_points = src_curves.points_for_curves(range); - const IndexRange dst_points = dst_curves.points_for_curves(range); - /* The arrays might be large, so a threaded copy might make sense here too. */ - dst.slice(dst_points).copy_from(src.slice(src_points)); - } - }); -} -static void copy_between_curves(const bke::CurvesGeometry &src_curves, - const bke::CurvesGeometry &dst_curves, - const Span<IndexRange> unselected_ranges, - const GSpan src, - const GMutableSpan dst) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src.typed<T>(), dst.typed<T>()); - }); -} - -/** - * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. - */ -static void fill_curve_counts(const bke::CurvesGeometry &src_curves, - const Span<IndexRange> curve_ranges, - MutableSpan<int> counts) -{ - threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange ranges_range) { - for (const IndexRange curves_range : curve_ranges.slice(ranges_range)) { - for (const int i : curves_range) { - counts[i] = src_curves.points_for_curve(i).size(); - } - } - }); -} - -/** - * Turn an array of sizes into the offset at each index including all previous sizes. - */ -static void accumulate_counts_to_offsets(MutableSpan<int> counts_to_offsets) -{ - int total = 0; - for (const int i : counts_to_offsets.index_range().drop_back(1)) { - const int count = counts_to_offsets[i]; - BLI_assert(count > 0); - counts_to_offsets[i] = total; - total += count; - } - counts_to_offsets.last() = total; -} - -/** - * Create new curves where the selected curves have been resampled with a number of uniform-length - * samples defined by the count field. Interpolate attributes to the result, with an accuracy that - * depends on the curve's resolution parameter. - * - * \warning The values provided by the #count_field must be 1 or greater. - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_uniform_count(const CurveComponent &src_component, - const Field<bool> &selection_field, - const Field<int> &count_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - /* Create the new curves without any points and evaluate the final count directly - * into the offsets array, in order to be accumulated into offsets later. */ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.add_with_destination(count_field, dst_offsets); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */ - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - - VArray<bool> curves_cyclic = src_curves.cyclic(); - VArray<int8_t> curve_types = src_curves.curve_types(); - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - src_curves.ensure_evaluated_lengths(); - - /* Sampling arbitrary attributes works by first interpolating them to the curve's standard - * "evaluated points" and then interpolating that result with the uniform samples. This is - * potentially wasteful when down-sampling a curve to many fewer points. There are two possible - * solutions: only sample the necessary points for interpolation, or first sample curve - * parameter/segment indices and evaluate the curve directly. */ - Array<int> sample_indices(dst_curves.points_num()); - Array<float> sample_factors(dst_curves.points_num()); - - /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on - * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a - * time or one curve at a time. */ - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - Vector<std::byte> evaluated_buffer; - - /* Gather uniform samples based on the accumulated lengths of the original curve. */ - for (const int i_curve : sliced_selection) { - const bool cyclic = curves_cyclic[i_curve]; - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::create_uniform_samples( - src_curves.evaluated_lengths_for_curve(i_curve, cyclic), - curves_cyclic[i_curve], - sample_indices.as_mutable_span().slice(dst_points), - sample_factors.as_mutable_span().slice(dst_points)); - } - - /* For every attribute, evaluate attributes from every curve in the range in the original - * curve's "evaluated points", then use linear interpolation to sample to the result. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - - if (curve_types[i_curve] == CURVE_TYPE_POLY) { - length_parameterize::linear_interpolation(src.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - else { - const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size(); - evaluated_buffer.clear(); - evaluated_buffer.resize(sizeof(T) * evaluated_size); - MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>(); - src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated); - - length_parameterize::linear_interpolation(evaluated.as_span(), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst.slice(dst_points)); - } - } - }); - } - - /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::linear_interpolation(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Evaluate each selected curve to its implicit evaluated points. - * - * \warning Curves with no evaluated points must not be selected. - */ -static Curves *resample_to_evaluated(const CurveComponent &src_component, - const Field<bool> &selection_field) -{ - const Curves &src_curves_id = *src_component.get_for_read(); - const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; - evaluator.set_selection(selection_field); - evaluator.evaluate(); - const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( - src_curves.curves_range(), nullptr); - - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - /* All resampled curves are poly curves. */ - dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY); - MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); - - src_curves.ensure_evaluated_offsets(); - threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) { - for (const int i : selection.slice(range)) { - dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size(); - } - }); - fill_curve_counts(src_curves, unselected_ranges, dst_offsets); - accumulate_counts_to_offsets(dst_offsets); - - dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - - /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); - MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); - - AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_component, dst_component, attributes); - - threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { - const IndexMask sliced_selection = selection.slice(selection_range); - - /* Evaluate generic point attributes directly to the result attributes. */ - for (const int i_attribute : attributes.dst.index_range()) { - attribute_math::convert_to_static_type(attributes.src[i_attribute].type(), [&](auto dummy) { - using T = decltype(dummy); - Span<T> src = attributes.src[i_attribute].typed<T>(); - MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>(); - - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - src_curves.interpolate_to_evaluated( - i_curve, src.slice(src_points), dst.slice(dst_points)); - } - }); - } - - /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); - } - - /* Fill the default value for non-interpolating attributes that still must be copied. */ - for (GMutableSpan dst : attributes.dst_no_interpolation) { - for (const int i_curve : sliced_selection) { - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size()); - } - } - }); - - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - copy_between_curves( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - copy_between_curves(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - copy_between_curves(src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); - - for (OutputAttribute &attribute : attributes.dst_attributes) { - attribute.save(); - } - - return dst_curves_id; -} - -/** - * Create a resampled curve point count field for both "uniform" options. - * The complexity is handled here in order to make the actual resampling functions simpler. - */ -static Field<int> get_curve_count_field(GeoNodeExecParams params, - const GeometryNodeCurveResampleMode mode) -{ - if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - static fn::CustomMF_SI_SO<int, int> max_one_fn( - "Clamp Above One", - [](int value) { return std::max(1, value); }, - fn::CustomMF_presets::AllSpanOrSingle()); - auto clamp_op = std::make_shared<FieldOperation>( - FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); - - return Field<int>(std::move(clamp_op)); - } - - if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( - "Length Input to Count", - [](const float curve_length, const float sample_length) { - /* Find the number of sampled segments by dividing the total length by - * the sample length. Then there is one more sampled point than segment. */ - const int count = int(curve_length / sample_length) + 1; - return std::max(1, count); - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto get_count_op = std::make_shared<FieldOperation>( - FieldOperation(get_count_fn, - {Field<float>(std::make_shared<SplineLengthFieldInput>()), - params.extract_input<Field<float>>("Length")})); - - return Field<int>(std::move(get_count_op)); - } - - BLI_assert_unreachable(); - return {}; -} - -/** - * Create a selection field that removes curves without any evaluated points (invalid NURBS curves) - * from the original selection provided to the node. This is here to simplify the sampling actual - * resampling code. - */ -static Field<bool> get_selection_field(GeoNodeExecParams params) -{ - static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn( - "Create Curve Selection", - [](const bool orig_selection, const int evaluated_points_num) { - return orig_selection && evaluated_points_num > 1; - }, - fn::CustomMF_presets::AllSpanOrSingle()); - - auto selection_op = std::make_shared<FieldOperation>( - FieldOperation(get_selection_fn, - {params.extract_input<Field<bool>>("Selection"), - Field<int>(std::make_shared<EvaluatedCountFieldInput>())})); - - return Field<bool>(std::move(selection_op)); -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -613,35 +58,38 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurveResample &storage = node_storage(params.node()); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - const Field<bool> selection = get_selection_field(params); + const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + } + } + }); + break; + } case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - Field<int> count = get_curve_count_field(params, mode); - - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + Field<float> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + } } - - Curves *result = resample_to_uniform_count( - *geometry_set.get_component_for_read<CurveComponent>(), selection, count); - - geometry_set.replace_curves(result); }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { + if (!component->is_empty()) { + geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + } } - - Curves *result = resample_to_evaluated( - *geometry_set.get_component_for_read<CurveComponent>(), selection); - - geometry_set.replace_curves(result); }); break; } 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 de29735bd2d..64a174df599 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -27,9 +27,9 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); 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 5d97720a4f8..ae36248b573 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 @@ -75,7 +75,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves) case CURVE_TYPE_CATMULL_ROM: { const int resolution = resolutions[i_curve]; for (const int i : IndexRange(points.size()).drop_back(1)) { - lengths[i + 1] = evaluated_lengths[resolution * i - 1]; + lengths[i + 1] = evaluated_lengths[resolution * i]; } break; } @@ -144,9 +144,9 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry return {}; } -static VArray<float> construct_curve_length_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), - const AttributeDomain domain) +static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGeometry &curves, + const IndexMask UNUSED(mask), + const AttributeDomain domain) { curves.ensure_evaluated_lengths(); @@ -217,9 +217,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { } }; -class CurveLengthFieldInput final : public GeometryFieldInput { +class CurveLengthParameterFieldInput final : public GeometryFieldInput { public: - CurveLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") { category_ = Category::Generated; } @@ -233,7 +233,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { if (curve_component.has_curves()) { const Curves &curves_id = *curve_component.get_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_length_varray(curves, mask, domain); + return construct_curve_length_parameter_varray(curves, mask, domain); } } return {}; @@ -247,7 +247,7 @@ class CurveLengthFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; + return dynamic_cast<const CurveLengthParameterFieldInput *>(&other) != nullptr; } }; @@ -288,7 +288,7 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; - Field<float> length_field{std::make_shared<CurveLengthFieldInput>()}; + Field<float> length_field{std::make_shared<CurveLengthParameterFieldInput>()}; Field<int> index_on_spline_field{std::make_shared<IndexOnSplineFieldInput>()}; params.set_output("Factor", std::move(parameter_field)); params.set_output("Length", std::move(length_field)); 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 4118448b237..500804e41f0 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 @@ -33,52 +33,49 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<class T> -static void scale_input_assign(const Span<T> input, +template<typename T> +static void scale_input_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(r_output.size())) { - r_output[i] = input[i * scale + offset]; + for (const int i : dst.index_range()) { + dst[i] = src[i * scale + offset]; } } -template<class T> -static void scale_output_assign(const Span<T> input, +template<typename T> +static void scale_output_assign(const Span<T> src, const int scale, const int offset, - const MutableSpan<T> &r_output) + MutableSpan<T> dst) { - for (const int i : IndexRange(input.size())) { - r_output[i * scale + offset] = input[i]; + for (const int i : src.index_range()) { + dst[i * scale + offset] = src[i]; } } -template<class T> -static void nurbs_to_bezier_assign(const Span<T> input, - const MutableSpan<T> r_output, - const KnotsMode knotsMode) +template<typename T> +static void nurbs_to_bezier_assign(const Span<T> src, + const MutableSpan<T> dst, + const KnotsMode knots_mode) { - const int input_size = input.size(); - const int output_size = r_output.size(); - - switch (knotsMode) { + switch (knots_mode) { case NURBS_KNOT_MODE_BEZIER: - scale_input_assign<T>(input, 3, 1, r_output); + scale_input_assign<T>(src, 3, 1, dst); break; case NURBS_KNOT_MODE_NORMAL: - for (const int i : IndexRange(output_size)) { - r_output[i] = input[(i + 1) % input_size]; + for (const int i : dst.index_range()) { + dst[i] = src[(i + 1) % src.size()]; } break; case NURBS_KNOT_MODE_ENDPOINT_BEZIER: case NURBS_KNOT_MODE_ENDPOINT: - for (const int i : IndexRange(1, output_size - 2)) { - r_output[i] = input[i + 1]; + for (const int i : dst.index_range().drop_back(1).drop_front(1)) { + dst[i] = src[i + 1]; } - r_output.first() = input.first(); - r_output.last() = input.last(); + dst.first() = src.first(); + dst.last() = src.last(); break; } } @@ -110,20 +107,20 @@ static void copy_attributes(const Spline &input_spline, Spline &output_spline, C static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions, const KnotsMode knots_mode) { - const int nurbs_positions_size = nurbs_positions.size(); + const int nurbs_positions_num = nurbs_positions.size(); Vector<float3> handle_positions; if (knots_mode == NURBS_KNOT_MODE_BEZIER) { - for (const int i : IndexRange(nurbs_positions_size)) { + for (const int i : IndexRange(nurbs_positions_num)) { if (i % 3 == 1) { continue; } handle_positions.append(nurbs_positions[i]); } - if (nurbs_positions_size % 3 == 1) { + if (nurbs_positions_num % 3 == 1) { handle_positions.pop_last(); } - else if (nurbs_positions_size % 3 == 2) { - const int last_index = nurbs_positions_size - 1; + else if (nurbs_positions_num % 3 == 2) { + const int last_index = nurbs_positions_num - 1; handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); } } @@ -137,11 +134,11 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]); handle_positions.append(nurbs_positions[1]); } - const int segments_size = nurbs_positions_size - 1; - const bool ignore_interior_segment = segments_size == 3 && is_periodic == false; + const int segments_num = nurbs_positions_num - 1; + const bool ignore_interior_segment = segments_num == 3 && is_periodic == false; if (ignore_interior_segment == false) { - const float mid_offset = (float)(segments_size - 1) / 2.0f; - for (const int i : IndexRange(1, segments_size - 2)) { + const float mid_offset = (float)(segments_num - 1) / 2.0f; + for (const int i : IndexRange(1, segments_num - 2)) { const int divisor = is_periodic ? 3 : std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); @@ -154,7 +151,7 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po } } } - const int last_index = nurbs_positions_size - 1; + const int last_index = nurbs_positions_num - 1; if (is_periodic) { handle_positions.append( nurbs_positions[last_index - 1] + @@ -357,7 +354,7 @@ static SplinePtr convert_to_nurbs(const Spline &input) static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSplineType &storage = node_storage(params.node()); - const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage.spline_type; + const GeometryNodeSplineType dst_type = (const GeometryNodeSplineType)storage.spline_type; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); @@ -371,11 +368,11 @@ static void node_geo_exec(GeoNodeExecParams params) 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); + const int domain_num = curve_component->attribute_domain_num(ATTR_DOMAIN_CURVE); Span<SplinePtr> src_splines = curve->splines(); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -386,7 +383,7 @@ static void node_geo_exec(GeoNodeExecParams params) threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { - switch (output_type) { + switch (dst_type) { case GEO_NODE_SPLINE_TYPE_POLY: new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; 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 371556c04f1..4d8745bf79e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -30,9 +30,9 @@ static Array<int> get_subdivided_offsets(const Spline &spline, const VArray<int> &cuts, const int spline_offset) { - Array<int> offsets(spline.segments_size() + 1); + Array<int> offsets(spline.segments_num() + 1); int offset = 0; - for (const int i : IndexRange(spline.segments_size())) { + for (const int i : IndexRange(spline.segments_num())) { offsets[i] = offset; offset = offset + std::max(cuts[spline_offset + i], 0) + 1; } @@ -46,8 +46,8 @@ static void subdivide_attribute(Span<T> src, const bool is_cyclic, MutableSpan<T> dst) { - const int src_size = src.size(); - threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + const int src_num = src.size(); + threading::parallel_for(IndexRange(src_num - 1), 1024, [&](IndexRange range) { for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; @@ -60,7 +60,7 @@ static void subdivide_attribute(Span<T> src, }); if (is_cyclic) { - const int i = src_size - 1; + const int i = src_num - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; @@ -86,7 +86,7 @@ static void subdivide_attribute(Span<T> src, static void subdivide_bezier_segment(const BezierSpline &src, const int index, const int offset, - const int result_size, + const int result_num, Span<float3> src_positions, Span<float3> src_handles_left, Span<float3> src_handles_right, @@ -106,11 +106,11 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR); - const float factor_delta = 1.0f / result_size; - for (const int cut : IndexRange(result_size)) { + const float factor_delta = 1.0f / result_num; + for (const int cut : IndexRange(result_num)) { const float factor = cut * factor_delta; dst_positions[offset + cut] = attribute_math::mix2( factor, src_positions[index], src_positions[next_index]); @@ -120,10 +120,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (is_last_cyclic_segment) { dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); - dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE); - const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num; /* Create a Bezier segment to update iteratively for every subdivision * and references to the meaningful values for ease of use. */ @@ -138,8 +138,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, handle_prev = src_handles_right[index]; handle_next = src_handles_left[next_index]; - for (const int cut : IndexRange(result_size - 1)) { - const float parameter = 1.0f / (result_size - cut); + for (const int cut : IndexRange(result_num - 1)) { + const float parameter = 1.0f / (result_num - cut); const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); /* Copy relevant temporary data to the result. */ @@ -154,7 +154,7 @@ static void subdivide_bezier_segment(const BezierSpline &src, } /* Copy the handles for the last segment from the temporary spline. */ - dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_right[offset + result_num - 1] = handle_prev; dst_handles_left[i_segment_last] = handle_next; } } @@ -287,9 +287,9 @@ static SplinePtr subdivide_spline(const Spline &spline, * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); - const int result_size = offsets.last() + int(!spline.is_cyclic()); + const int result_num = offsets.last() + int(!spline.is_cyclic()); SplinePtr new_spline = spline.copy_only_settings(); - new_spline->resize(result_size); + new_spline->resize(result_num); subdivide_builtin_attributes(spline, offsets, *new_spline); subdivide_dynamic_attributes(spline, offsets, *new_spline); return new_spline; @@ -334,9 +334,9 @@ static void node_geo_exec(GeoNodeExecParams params) const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray<int> &cuts = evaluator.get_evaluated<int>(0); 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 894580f2932..73b2c400e90 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 @@ -90,7 +90,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += count; } } @@ -104,7 +104,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - if (splines[i]->evaluated_points_size() > 0) { + if (splines[i]->evaluated_points_num() > 0) { offset += splines[i]->length() / resolution + 1; } } @@ -120,7 +120,7 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } /** - * \note: Relies on the fact that all attributes on point clouds are stored contiguously. + * \note Relies on the fact that all attributes on point clouds are stored contiguously. */ static GMutableSpan ensure_point_attribute(PointCloudComponent &points, const AttributeIDRef &attribute_id, @@ -240,18 +240,18 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, for (const int i : range) { const Spline &spline = *splines[i]; const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size == 0) { + const int num = offsets[i + 1] - offsets[i]; + if (num == 0) { continue; } - const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), uniform_samples, - data.radii.slice(offset, size)); + data.radii.slice(offset, num)); for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; @@ -260,14 +260,13 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), - uniform_samples, - dst.slice(offset, size)); + spline.sample_with_index_factors( + spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); } if (!data.tangents.is_empty()) { Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, size); + MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); for (float3 &vector : sampled_tangents) { vector = math::normalize(vector); @@ -276,7 +275,7 @@ static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, if (!data.normals.is_empty()) { Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, size); + MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); for (float3 &vector : sampled_normals) { vector = math::normalize(vector); @@ -298,8 +297,8 @@ static void copy_spline_domain_attributes(const CurveEval &curve, for (const int i : curve.splines().index_range()) { const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], size); + const int num = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], num); } return true; @@ -329,13 +328,13 @@ static void node_geo_exec(GeoNodeExecParams params) curve->assert_valid_point_attributes(); const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { + const int total_num = offsets.last(); + if (total_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( points, *curve, attribute_outputs); 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 df360818313..c993a3d305d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -117,10 +117,10 @@ struct TrimLocation { }; template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size) +static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) { - BLI_assert(start_index + size - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * size); + BLI_assert(start_index + num - 1 <= data.size()); + memmove(data.data(), &data[start_index], sizeof(T) * num); } /* Shift slice to start of span and modifies start and end data. */ @@ -129,17 +129,17 @@ static void linear_trim_data(const TrimLocation &start, const TrimLocation &end, MutableSpan<T> data) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, size); + shift_slice_to_start<T>(data, start.left_index, num); } const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]); + const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); data.first() = start_data; - data[size - 1] = end_data; + data[num - 1] = end_data; } /** @@ -152,12 +152,12 @@ static void linear_trim_to_output_data(const TrimLocation &start, Span<T> src, MutableSpan<T> dst) { - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - dst.copy_from(src.slice(start.left_index, size)); + dst.copy_from(src.slice(start.left_index, num)); dst.first() = start_data; dst.last() = end_data; } @@ -175,8 +175,8 @@ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lo const int right = left == (spline.size() - 1) ? 0 : left + 1; const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_size = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); + const int segment_eval_num = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); return {left, right, factor}; } @@ -191,7 +191,7 @@ static void trim_poly_spline(Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; linear_trim_data<float3>(start, end, spline.positions()); linear_trim_data<float>(start, end, spline.radii()); @@ -209,7 +209,7 @@ static void trim_poly_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - spline.resize(size); + spline.resize(num); } /** @@ -225,11 +225,11 @@ static PolySpline trim_nurbs_spline(const Spline &spline, const TrimLocation end = { end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Create poly spline and copy trimmed data to it. */ PolySpline new_spline; - new_spline.resize(size); + new_spline.resize(num); /* Copy generic attribute data. */ spline.attributes.foreach_attribute( @@ -283,7 +283,7 @@ static void trim_bezier_spline(Spline &spline, const Span<int> control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ - const int size = end.right_index - start.left_index + 1; + const int num = end.right_index - start.left_index + 1; /* Trim the spline attributes. Done before end.factor recalculation as it needs * the original end.factor value. */ @@ -301,10 +301,10 @@ static void trim_bezier_spline(Spline &spline, }, ATTR_DOMAIN_POINT); - /* Recalculate end.factor if the size is two, because the adjustment in the + /* Recalculate end.factor if the `num` is two, because the adjustment in the * position of the control point of the spline to the left of the new end point will change the * factor between them. */ - if (size == 2) { + if (num == 2) { if (start_lookup.factor == 1.0f) { end.factor = 0.0f; } @@ -328,38 +328,38 @@ static void trim_bezier_spline(Spline &spline, const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( end.left_index, end.right_index, end.factor); - /* If size is two, then the start point right handle needs to change to reflect the end point + /* If `num` is two, then the start point right handle needs to change to reflect the end point * previous handle update. */ - if (size == 2) { + if (num == 2) { start_point.right_handle = end_point.handle_prev; } /* Shift control point position data to start at beginning of array. */ if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size); + shift_slice_to_start(bezier_spline.positions(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); + shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); } bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[size - 1] = end_point.position; + bezier_spline.positions()[num - 1] = end_point.position; bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; /* If there is at least one control point between the endpoints, update the control * point handle to the right of the start point and to the left of the end point. */ - if (size > 2) { + if (num > 2) { bezier_spline.handle_positions_left()[start.right_index - start.left_index] = start_point.handle_next; bezier_spline.handle_positions_right()[end.left_index - start.left_index] = end_point.handle_prev; } - bezier_spline.resize(size); + bezier_spline.resize(num); } static void trim_spline(SplinePtr &spline, @@ -506,9 +506,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); @@ -527,7 +527,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, continue; } - if (spline->evaluated_edges_size() == 0) { + if (spline->evaluated_edges_num() == 0) { continue; } 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 8a0c900fbde..99edc4d298c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -470,7 +470,7 @@ static void separate_curve_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -493,7 +493,7 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; fn::FieldEvaluator selection_evaluator{field_context, - src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + src_points.attribute_domain_num(ATTR_DOMAIN_POINT)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); @@ -526,8 +526,8 @@ static void separate_instance_selection(GeometrySet &geometry_set, InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0); @@ -1238,7 +1238,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, GeometryComponentFieldContext field_context{src_component, selection_domain}; fn::FieldEvaluator selection_evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + src_component.attribute_domain_num(selection_domain)}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); 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 d9f29d1ef1c..c242cfd5cf3 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 @@ -398,11 +398,11 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent { const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); + const int domain_num = component.attribute_domain_num(attribute_domain); - Array<float> densities(domain_size, 0.0f); + Array<float> densities(domain_num, 0.0f); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index db62ad16b24..ebf0c450605 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -359,19 +359,19 @@ static void duplicate_curves(GeometrySet &geometry_set, Array<int> curve_offsets(selection.size() + 1); Array<int> point_offsets(selection.size() + 1); - int dst_curves_size = 0; - int dst_points_size = 0; + int dst_curves_num = 0; + int dst_points_num = 0; for (const int i_curve : selection.index_range()) { const int count = std::max(counts[selection[i_curve]], 0); - curve_offsets[i_curve] = dst_curves_size; - point_offsets[i_curve] = dst_points_size; - dst_curves_size += count; - dst_points_size += count * curves.points_for_curve(selection[i_curve]).size(); + curve_offsets[i_curve] = dst_curves_num; + point_offsets[i_curve] = dst_points_num; + dst_curves_num += count; + dst_points_num += count * curves.points_for_curve(selection[i_curve]).size(); } - curve_offsets.last() = dst_curves_size; - point_offsets.last() = dst_points_size; + curve_offsets.last() = dst_curves_num; + point_offsets.last() = dst_points_num; - Curves *new_curves_id = bke::curves_new_nomain(dst_points_size, dst_curves_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write(); @@ -387,7 +387,7 @@ static void duplicate_curves(GeometrySet &geometry_set, } } }); - all_dst_offsets.last() = dst_points_size; + all_dst_offsets.last() = dst_points_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -818,23 +818,23 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); Array<int> offsets = accumulate_counts_to_offsets(selection, counts); - const int dst_size = offsets.last(); + const int dst_num = offsets.last(); Array<int> point_to_curve_map(src_curves.points_num()); threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) { for (const int i_curve : range) { - const IndexRange point_range = src_curves.points_for_curve(i_curve); - point_to_curve_map.as_mutable_span().slice(point_range).fill(i_curve); + const IndexRange points = src_curves.points_for_curve(i_curve); + point_to_curve_map.as_mutable_span().slice(points).fill(i_curve); } }); - Curves *new_curves_id = bke::curves_new_nomain(dst_size, dst_size); + Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { new_curve_offsets[i] = i; } - new_curve_offsets.last() = dst_size; + new_curve_offsets.last() = dst_num; CurveComponent dst_component; dst_component.replace(new_curves_id, GeometryOwnershipType::Editable); @@ -956,10 +956,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, { const PointCloudComponent &src_points = *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_size}; + FieldEvaluator evaluator{field_context, point_num}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -1047,7 +1047,7 @@ static void duplicate_instances(GeometrySet &geometry_set, *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; - FieldEvaluator evaluator{field_context, src_instances.instances_amount()}; + FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); 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 84acab47661..94fbec66bfe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -57,8 +57,8 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); 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 4591cfa1da2..1f16e8d55bd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -109,7 +109,7 @@ static MutableSpan<MLoop> mesh_loops(Mesh &mesh) } /** - * \note: Some areas in this file rely on the new sections of attributes from #CustomData_realloc + * \note Some areas in this file rely on the new sections of attributes from #CustomData_realloc * to be zeroed. */ static void expand_mesh(Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 77be98f169e..bf956f3b83d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -91,7 +91,7 @@ class FieldAtIndex final : public GeometryFieldInput { { const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; + component.attribute_domain_num(value_field_domain_)}; value_evaluator.add(value_field_); value_evaluator.evaluate(); const GVArray &values = value_evaluator.get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 34c3169065c..0484017cf3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -22,11 +22,11 @@ static void node_declare(NodeDeclarationBuilder &b) static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index d39291a2a7e..8e3a9b6769d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -101,8 +101,7 @@ class IslandCountFieldInput final : public GeometryFieldInput { island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh_component.attribute_domain_num(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc index 72dfff7cb39..553ea6cfcf0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc @@ -82,6 +82,11 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_default_remaining_outputs(); return; } + if (!bke::allow_procedural_attribute_access(name)) { + params.error_message_add(NodeWarningType::Info, TIP_(bke::no_procedural_access_message)); + params.set_default_remaining_outputs(); + return; + } params.used_named_attribute(name, NamedAttributeUsage::Read); 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 6c24f86b63b..84d773ff8eb 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 @@ -4,71 +4,6 @@ #include "BKE_curves.hh" -namespace blender::nodes { - -/* -------------------------------------------------------------------- - * Spline Length - */ - -static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, - const AttributeDomain domain) -{ - if (!component.has_curves()) { - return {}; - } - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - - curves.ensure_evaluated_lengths(); - - VArray<bool> cyclic = curves.cyclic(); - VArray<float> lengths = VArray<float>::ForFunc( - curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) { - return curves.evaluated_length_total_for_curve(index, cyclic[index]); - }); - - if (domain == ATTR_DOMAIN_CURVE) { - return lengths; - } - - if (domain == ATTR_DOMAIN_POINT) { - return component.attribute_try_adapt_domain<float>( - std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); - } - - return {}; -} - -SplineLengthFieldInput::SplineLengthFieldInput() - : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") -{ - category_ = Category::Generated; -} - -GVArray SplineLengthFieldInput::get_varray_for_context(const GeometryComponent &component, - const AttributeDomain domain, - IndexMask UNUSED(mask)) const -{ - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_length_gvarray(curve_component, domain); - } - return {}; -} - -uint64_t SplineLengthFieldInput::hash() const -{ - /* Some random constant hash. */ - return 3549623580; -} - -bool SplineLengthFieldInput::is_equal_to(const fn::FieldNode &other) const -{ - return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; -} - -} // namespace blender::nodes - namespace blender::nodes::node_geo_input_spline_length_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -81,8 +16,8 @@ static void node_declare(NodeDeclarationBuilder &b) * Spline Count */ -static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, - const AttributeDomain domain) +static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component, + const AttributeDomain domain) { if (!component.has_curves()) { return {}; @@ -117,7 +52,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_spline_count_gvarray(curve_component, domain); + return construct_curve_point_count_gvarray(curve_component, domain); } return {}; } @@ -136,7 +71,7 @@ class SplineCountFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { - Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()}; + Field<float> spline_length_field{std::make_shared<bke::CurveLengthFieldInput>()}; Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()}; params.set_output("Length", std::move(spline_length_field)); 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 61f719ade4e..12582f9e9c6 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 @@ -50,7 +50,7 @@ static void add_instances_from_component( const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; - const int domain_size = src_component.attribute_domain_size(domain); + const int domain_num = src_component.attribute_domain_num(domain); VArray<bool> pick_instance; VArray<int> indices; @@ -59,7 +59,7 @@ static void add_instances_from_component( GeometryComponentFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ @@ -73,7 +73,7 @@ static void add_instances_from_component( /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ - const int start_len = dst_component.instances_amount(); + const int start_len = dst_component.instances_num(); const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); @@ -119,12 +119,12 @@ static void add_instances_from_component( const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { - const int src_instances_amount = src_instances->instances_amount(); + const int src_instances_num = src_instances->instances_num(); const int original_index = indices[i]; /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 * refers to the last element. */ - const int index = mod_i(original_index, std::max(src_instances_amount, 1)); - if (index < src_instances_amount) { + const int index = mod_i(original_index, std::max(src_instances_num, 1)); + if (index < src_instances_num) { /* Get the reference to the source instance. */ const int src_handle = src_instances->instance_reference_handles()[index]; dst_handle = handle_mapping[src_handle]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index e35f152ce49..2126a5cc329 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -40,9 +40,9 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 0e2803cd035..2067086c298 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -56,8 +56,8 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, int offset = 0; for (const GeometryComponent *component : src_components) { - const int domain_size = component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component->attribute_domain_num(domain); + if (domain_num == 0) { continue; } GVArray read_attribute = component->attribute_get_for_read( @@ -66,9 +66,9 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, GVArray_GSpan src_span{read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; - cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size); + cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num); - offset += domain_size; + offset += domain_num; } } @@ -101,7 +101,7 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_amount(); + tot_instances += src_component->instances_num(); } dst_component.reserve(tot_instances); diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index 1ec97858d4d..1def4089115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -39,9 +39,9 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p const float merge_distance, const Field<bool> &selection_field) { - const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); @@ -57,10 +57,10 @@ static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponen const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_size); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + Array<bool> selection(src_num); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); @@ -72,9 +72,9 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes const float merge_distance, const Field<bool> &selection_field) { - const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_size}; + FieldEvaluator evaluator{context, src_num}; evaluator.add(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 636ecb8ab41..0029b547375 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -6,414 +6,9 @@ #include "BKE_material.h" #include "BKE_mesh.h" -#include "node_geometry_util.hh" - -namespace blender::nodes { - -struct CuboidConfig { - float3 size; - int verts_x; - int verts_y; - int verts_z; - int edges_x; - int edges_y; - int edges_z; - int vertex_count; - int poly_count; - int loop_count; - - CuboidConfig(float3 size, int verts_x, int verts_y, int verts_z) - : size(size), - verts_x(verts_x), - verts_y(verts_y), - verts_z(verts_z), - edges_x(verts_x - 1), - edges_y(verts_y - 1), - edges_z(verts_z - 1) - { - BLI_assert(edges_x > 0 && edges_y > 0 && edges_z > 0); - this->vertex_count = this->get_vertex_count(); - this->poly_count = this->get_poly_count(); - this->loop_count = this->poly_count * 4; - } - - private: - int get_vertex_count() - { - const int inner_position_count = (verts_x - 2) * (verts_y - 2) * (verts_z - 2); - return verts_x * verts_y * verts_z - inner_position_count; - } - - int get_poly_count() - { - return 2 * (edges_x * edges_y + edges_y * edges_z + edges_z * edges_x); - } -}; - -static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts) -{ - const float z_bottom = -config.size.z / 2.0f; - const float z_delta = config.size.z / config.edges_z; - - const float x_left = -config.size.x / 2.0f; - const float x_delta = config.size.x / config.edges_x; - - const float y_front = -config.size.y / 2.0f; - const float y_delta = config.size.y / config.edges_y; - - int vert_index = 0; - - for (const int z : IndexRange(config.verts_z)) { - if (ELEM(z, 0, config.edges_z)) { - /* Fill bottom and top. */ - const float z_pos = z_bottom + z_delta * z; - for (const int y : IndexRange(config.verts_y)) { - const float y_pos = y_front + y_delta * y; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - } - else { - for (const int y : IndexRange(config.verts_y)) { - if (ELEM(y, 0, config.edges_y)) { - /* Fill y-sides. */ - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - for (const int x : IndexRange(config.verts_x)) { - const float x_pos = x_left + x_delta * x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - } - } - else { - /* Fill x-sides. */ - const float x_pos = x_left; - const float y_pos = y_front + y_delta * y; - const float z_pos = z_bottom + z_delta * z; - copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); - const float x_pos2 = x_left + x_delta * config.edges_x; - copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); - } - } - } - } -} - -/* vert_1 = bottom left, vert_2 = bottom right, vert_3 = top right, vert_4 = top left. - * Hence they are passed as 1,4,3,2 when calculating polys clockwise, and 1,2,3,4 for - * anti-clockwise. - */ -static void define_quad(MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops, - const int poly_index, - const int loop_index, - const int vert_1, - const int vert_2, - const int vert_3, - const int vert_4) -{ - MPoly &poly = polys[poly_index]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_1 = loops[loop_index]; - loop_1.v = vert_1; - MLoop &loop_2 = loops[loop_index + 1]; - loop_2.v = vert_2; - MLoop &loop_3 = loops[loop_index + 2]; - loop_3.v = vert_3; - MLoop &loop_4 = loops[loop_index + 3]; - loop_4.v = vert_4; -} - -static void calculate_polys(const CuboidConfig &config, - MutableSpan<MPoly> polys, - MutableSpan<MLoop> loops) -{ - int loop_index = 0; - int poly_index = 0; - - /* Number of vertices in an XY cross-section of the cube (barring top and bottom faces). */ - const int xy_cross_section_vert_count = config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2); +#include "GEO_mesh_primitive_cuboid.hh" - /* Calculate polys for Bottom faces. */ - int vert_1_start = 0; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - const int vert_1 = vert_1_start + x; - const int vert_2 = vert_1_start + config.verts_x + x; - const int vert_3 = vert_2 + 1; - const int vert_4 = vert_1 + 1; - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - vert_1_start += config.verts_x; - } - - /* Calculate polys for Front faces. */ - vert_1_start = 0; - int vert_2_start = config.verts_x * config.verts_y; - - for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_1_start = vert_2_start; - vert_2_start += config.verts_x * config.verts_y - (config.verts_x - 2) * (config.verts_y - 2); - } - - /* Calculate polys for Top faces. */ - vert_1_start = config.verts_x * config.verts_y + - (config.verts_z - 2) * (config.verts_x * config.verts_y - - (config.verts_x - 2) * (config.verts_y - 2)); - vert_2_start = vert_1_start + config.verts_x; - - for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_1_start + x + 1, - vert_2_start + x + 1, - vert_2_start + x); - loop_index += 4; - poly_index++; - } - vert_2_start += config.verts_x; - vert_1_start += config.verts_x; - } - - /* Calculate polys for Back faces. */ - vert_1_start = config.verts_x * config.edges_y; - vert_2_start = vert_1_start + xy_cross_section_vert_count; - - for (const int z : IndexRange(config.edges_z)) { - if (z == (config.edges_z - 1)) { - vert_2_start += (config.verts_x - 2) * (config.verts_y - 2); - } - for (const int x : IndexRange(config.edges_x)) { - define_quad(polys, - loops, - poly_index, - loop_index, - vert_1_start + x, - vert_2_start + x, - vert_2_start + x + 1, - vert_1_start + x + 1); - loop_index += 4; - poly_index++; - } - vert_2_start += xy_cross_section_vert_count; - vert_1_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Left faces. */ - vert_1_start = 0; - vert_2_start = config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1; - int vert_2; - int vert_3; - int vert_4; - - if (z == 0 || y == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_1 += config.verts_x - 2; - vert_4 = vert_1 + 2; - } - - if (y == 0 || z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_2 += config.verts_x - 2; - vert_3 = vert_2 + 2; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_2, vert_3, vert_4); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } - - /* Calculate polys for Right faces. */ - vert_1_start = config.edges_x; - vert_2_start = vert_1_start + config.verts_x * config.verts_y; - - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - int vert_1 = vert_1_start; - int vert_2 = vert_2_start; - int vert_3 = vert_2_start + 2; - int vert_4 = vert_1 + config.verts_x; - - if (z == 0) { - vert_1 = vert_1_start + config.verts_x * y; - vert_4 = vert_1 + config.verts_x; - } - else { - vert_1 = vert_1_start + 2 * y; - vert_4 = vert_1 + 2; - } - - if (z == (config.edges_z - 1)) { - vert_2 = vert_2_start + config.verts_x * y; - vert_3 = vert_2 + config.verts_x; - } - else { - vert_2 = vert_2_start + 2 * y; - vert_3 = vert_2 + 2; - } - - if (y == (config.edges_y - 1)) { - vert_3 = vert_2 + config.verts_x; - vert_4 = vert_1 + config.verts_x; - } - - define_quad(polys, loops, poly_index, loop_index, vert_1, vert_4, vert_3, vert_2); - loop_index += 4; - poly_index++; - } - if (z == 0) { - vert_1_start += config.verts_x * config.verts_y; - } - else { - vert_1_start += xy_cross_section_vert_count; - } - vert_2_start += xy_cross_section_vert_count; - } -} - -static void calculate_uvs(const CuboidConfig &config, Mesh *mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttribute_Typed<float2> uv_attribute = - mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); - MutableSpan<float2> uvs = uv_attribute.as_span(); - - int loop_index = 0; - - const float x_delta = 0.25f / static_cast<float>(config.edges_x); - const float y_delta = 0.25f / static_cast<float>(config.edges_y); - const float z_delta = 0.25f / static_cast<float>(config.edges_z); - - /* Calculate bottom face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - y * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f - y * y_delta); - } - } - - /* Calculate front face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.375f + (z + 1) * z_delta); - } - } - - /* Calculate top face UVs. */ - for (const int y : IndexRange(config.edges_y)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + y * y_delta); - uvs[loop_index++] = float2(0.25f + (x + 1) * x_delta, 0.625f + (y + 1) * y_delta); - uvs[loop_index++] = float2(0.25f + x * x_delta, 0.625f + (y + 1) * y_delta); - } - } - - /* Calculate back face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int x : IndexRange(config.edges_x)) { - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(1.0f - x * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(1.0f - (x + 1) * x_delta, 0.375f + z * z_delta); - } - } - - /* Calculate left face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.25f - y * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.25f - (y + 1) * y_delta, 0.375f + z * z_delta); - } - } - - /* Calculate right face UVs. */ - for (const int z : IndexRange(config.edges_z)) { - for (const int y : IndexRange(config.edges_y)) { - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + z * z_delta); - uvs[loop_index++] = float2(0.50f + (y + 1) * y_delta, 0.375f + (z + 1) * z_delta); - uvs[loop_index++] = float2(0.50f + y * y_delta, 0.375f + (z + 1) * z_delta); - } - } - - uv_attribute.save(); -} - -Mesh *create_cuboid_mesh(const float3 size, - const int verts_x, - const int verts_y, - const int verts_z) -{ - const CuboidConfig config(size, verts_x, verts_y, verts_z); - - Mesh *mesh = BKE_mesh_new_nomain( - config.vertex_count, 0, 0, config.loop_count, config.poly_count); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - - calculate_vertices(config, {mesh->mvert, mesh->totvert}); - - calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); - BKE_mesh_calc_edges(mesh, false, false); - - calculate_uvs(config, mesh); - - return mesh; -} - -} // namespace blender::nodes +#include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_cube_cc { @@ -442,6 +37,16 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } +static Mesh *create_cuboid_mesh(const float3 &size, + const int verts_x, + const int verts_y, + const int verts_z) +{ + Mesh *mesh = geometry::create_cuboid_mesh(size, verts_x, verts_y, verts_z, "uv_map"); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + return mesh; +} + static Mesh *create_cube_mesh(const float3 size, const int verts_x, const int verts_y, 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 f6ee3d00dee..ec6acf55dd8 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 @@ -25,7 +25,7 @@ static void node_geo_exec(GeoNodeExecParams params) const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 1a0cc53cb6c..6b23b685549 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -41,14 +43,15 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -template<typename T> -static void copy_attribute_to_points(const VArray<T> &src, - const IndexMask mask, - MutableSpan<T> dst) +static void materialize_compressed_to_uninitialized_threaded(const GVArray &src, + const IndexMask mask, + GMutableSpan dst) { - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } + BLI_assert(src.type() == dst.type()); + BLI_assert(mask.size() == dst.size()); + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data()); + }); } static void geometry_set_mesh_to_points(GeometrySet &geometry_set, @@ -63,12 +66,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, return; } GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_size = mesh_component->attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = mesh_component->attribute_domain_num(domain); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no @@ -79,16 +82,21 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); geometry_set.replace_pointcloud(pointcloud); PointCloudComponent &point_component = geometry_set.get_component_for_write<PointCloudComponent>(); - copy_attribute_to_points(evaluator.get_evaluated<float3>(0), - selection, - {(float3 *)pointcloud->co, pointcloud->totpoint}); - copy_attribute_to_points( - evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint}); + OutputAttribute position = point_component.attribute_try_get_for_output_only( + "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(0), selection, position.as_span()); + position.save(); + + OutputAttribute radius = point_component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT); + materialize_compressed_to_uninitialized_threaded( + evaluator.get_evaluated(1), selection, radius.as_span()); + radius.save(); Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( @@ -102,11 +110,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, OutputAttribute dst = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src_typed = src.typed<T>(); - copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>()); - }); + materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span()); dst.save(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 0f6586617bc..577b001fd06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -38,13 +38,13 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + fn::FieldEvaluator selection_evaluator{field_context, domain_num}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); 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 c99b51ffd4c..42cee4c0efe 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 @@ -168,14 +168,14 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, Field<float> radius_field = params.get_input<Field<float>>("Radius"); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); - r_positions.resize(r_positions.size() + domain_size); - positions.materialize(r_positions.as_mutable_span().take_back(domain_size)); + r_positions.resize(r_positions.size() + domain_num); + positions.materialize(r_positions.as_mutable_span().take_back(domain_num)); - r_radii.resize(r_radii.size() + domain_size); - fn::FieldEvaluator evaluator{field_context, domain_size}; - evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + r_radii.resize(r_radii.size() + domain_num); + fn::FieldEvaluator evaluator{field_context, domain_num}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_num)); evaluator.evaluate(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 368954447c9..0c30d50076f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -312,8 +312,8 @@ class RaycastFunction : public fn::MultiFunction { } const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); target_data_ = &target_evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc index effeac5a37f..ecda35f6363 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc @@ -21,6 +21,11 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); return; } + if (!bke::allow_procedural_attribute_access(name)) { + params.error_message_add(NodeWarningType::Info, TIP_(bke::no_procedural_access_message)); + params.set_output("Geometry", std::move(geometry_set)); + return; + } std::atomic<bool> attribute_exists = false; std::atomic<bool> cannot_delete = false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 6eb95859e50..59e203afd08 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -19,9 +19,9 @@ static void node_declare(NodeDeclarationBuilder &b) static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances_component.instances_amount(); + const int domain_num = instances_component.instances_num(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 4ca21874f8f..d4716a6b6f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -23,7 +23,7 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); 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 73e49c7d037..d2082924fa7 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 @@ -75,12 +75,12 @@ static void set_position_in_component(CurveComponent &component, const Field<float3> &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_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 a23a6c09551..4c84093bfcb 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 @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); 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 1155c97dc38..8b1e5935a61 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 @@ -17,15 +17,15 @@ static void set_tilt_in_component(GeometryComponent &component, const Field<float> &tilt_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>( "tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index 0892e068ce2..ec95f9a89f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -20,12 +20,12 @@ static void set_id_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); /* Since adding the ID attribute can change the result of the field evaluation (the random value diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a0b38209f97..58613dae832 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -17,15 +17,15 @@ static void set_material_index_in_component(GeometryComponent &component, const Field<int> &index_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>( "material_index", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(index_field, indices.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index 93024fd81d6..571bead9743 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -21,15 +21,15 @@ static void set_radius_in_component(GeometryComponent &component, const Field<float> &radius_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT); + if (domain_num == 0) { return; } OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( "radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray()); evaluator.evaluate(); 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 d2ff9753897..caf33108716 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -143,12 +143,12 @@ static void set_position_in_component(GeometryComponent &component, ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(domain); + if (domain_num == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index 3420e17cc10..b98fbd0a0fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -17,15 +17,15 @@ static void set_smooth_in_component(GeometryComponent &component, const Field<bool> &shade_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>( "shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(shade_field, shades.varray()); evaluator.evaluate(); 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 dc7f3b1343a..976857883f0 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 @@ -17,15 +17,15 @@ static void set_cyclic_in_component(GeometryComponent &component, const Field<bool> &cyclic_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>( "cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray()); evaluator.evaluate(); 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 f3031ff3678..8b665376c01 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 @@ -17,15 +17,15 @@ static void set_resolution_in_component(GeometryComponent &component, const Field<int> &resolution_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE); + if (domain_num == 0) { return; } OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>( "resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray()); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index de206be5367..669740f27cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -88,8 +88,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, const GField &field) { GeometryComponentFieldContext field_context{component, domain}; - const int domain_size = component.attribute_domain_size(domain); - const IndexMask mask{IndexMask(domain_size)}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; const CPPType &type = field.cpp_type(); const CustomDataType data_type = bke::cpp_type_to_custom_data_type(type); @@ -97,10 +97,10 @@ static void try_capture_field_on_geometry(GeometryComponent &component, /* Could avoid allocating a new buffer if: * - We are writing to an attribute that exists already. * - The field does not depend on that attribute (we can't easily check for that yet). */ - void *buffer = MEM_mallocN(type.size() * domain_size, __func__); + void *buffer = MEM_mallocN(type.size() * domain_num, __func__); fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size}); + evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num}); evaluator.evaluate(); component.attribute_try_delete(name); @@ -114,7 +114,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, else { /* Cannot change type of built-in attribute. */ } - type.destruct_n(buffer, domain_size); + type.destruct_n(buffer, domain_num); MEM_freeN(buffer); } else { @@ -131,6 +131,11 @@ static void node_geo_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); return; } + if (!bke::allow_procedural_attribute_access(name)) { + params.error_message_add(NodeWarningType::Info, TIP_(bke::no_procedural_access_message)); + params.set_output("Geometry", std::move(geometry_set)); + return; + } params.used_named_attribute(name, NamedAttributeUsage::Write); 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 658de02fdab..33f5eccd719 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 @@ -97,12 +97,8 @@ static void node_update(bNodeTree *ntree, bNode *node) ntree, socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; - bNodeSocket *width_socket = height_socket->prev; nodeSetSocketAvailability( ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); - node_sock_label(width_socket, - overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") : - N_("Text Box Width")); } static float3 get_pivot_point(GeoNodeExecParams ¶ms, CurveEval &curve) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4832feac5bd..9eda5bb34ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -21,7 +23,13 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); - b.add_input<decl::Float>(N_("Crease")) + b.add_input<decl::Float>(N_("Edge Crease")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Vertex Crease")) .default_value(0.0f) .min(0.0f) .max(1.0f) @@ -44,6 +52,45 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } +#ifdef WITH_OPENSUBDIV +static void materialize_and_clamp_creases(const VArray<float> &crease_varray, + MutableSpan<float> creases) +{ + threading::parallel_for(creases.index_range(), 1024, [&](IndexRange range) { + crease_varray.materialize(range, creases); + for (const int i : range) { + creases[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + }); +} + +static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) +{ + float *crease; + if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) { + crease = static_cast<float *>(CustomData_get_layer(&mesh.vdata, CD_CREASE)); + } + else { + crease = static_cast<float *>( + CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert)); + } + materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert}); +} + +static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray) +{ + OutputAttribute_Typed<float> attribute = mesh.attribute_try_get_for_output_only<float>( + "crease", ATTR_DOMAIN_EDGE); + materialize_and_clamp_creases(crease_varray, attribute.as_span()); + attribute.save(); +} + +static bool varray_is_nonzero(const VArray<float> &varray) +{ + return !(varray.is_single() && varray.get_internal_single() == 0.0f); +} +#endif + static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); @@ -51,7 +98,8 @@ static void node_geo_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else - Field<float> crease_field = params.extract_input<Field<float>>("Crease"); + Field<float> edge_crease_field = params.extract_input<Field<float>>("Edge Crease"); + Field<float> vertex_crease_field = params.extract_input<Field<float>>("Vertex Crease"); const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); const int uv_smooth = storage.uv_smooth; @@ -69,27 +117,31 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - AttributeDomain domain = ATTR_DOMAIN_EDGE; - GeometryComponentFieldContext field_context{mesh_component, domain}; - const int domain_size = mesh_component.attribute_domain_size(domain); - - if (domain_size == 0) { + const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + if (verts_num == 0 || edges_num == 0) { return; } - FieldEvaluator evaluator(field_context, domain_size); - evaluator.add(crease_field); - evaluator.evaluate(); - const VArray<float> &creases = evaluator.get_evaluated<float>(0); + GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT}; + FieldEvaluator point_evaluator(point_context, verts_num); + point_evaluator.add(vertex_crease_field); + point_evaluator.evaluate(); + const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0); + + GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator(edge_context, edges_num); + edge_evaluator.add(edge_crease_field); + edge_evaluator.evaluate(); + const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0); + + const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases); - OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>( - "crease", domain); - MutableSpan<float> crease_span = crease.as_span(); - for (auto i : creases.index_range()) { - crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + if (use_creases) { + write_vertex_creases(*geometry_set.get_mesh_for_write(), vertex_creases); + write_edge_creases(geometry_set.get_component_for_write<MeshComponent>(), edge_creases); } - crease.save(); /* Initialize mesh settings. */ SubdivToMeshSettings mesh_settings; @@ -100,7 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params) SubdivSettings subdiv_settings; subdiv_settings.is_simple = false; subdiv_settings.is_adaptive = false; - subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.use_creases = use_creases; subdiv_settings.level = subdiv_level; subdiv_settings.vtx_boundary_interpolation = @@ -108,19 +160,19 @@ static void node_geo_exec(GeoNodeExecParams params) subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( uv_smooth); - Mesh *mesh_in = mesh_component.get_for_write(); + const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in); /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { return; } - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in); - mesh_component.replace(mesh_out); + geometry_set.replace_mesh(mesh_out); BKE_subdiv_free(subdiv); }); 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 12e306ba480..dca214660c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -365,7 +365,7 @@ static bool component_is_available(const GeometrySet &geometry, if (component.is_empty()) { return false; } - return component.attribute_domain_size(domain) != 0; + return component.attribute_domain_num(domain) != 0; } /** @@ -433,8 +433,8 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction { { const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + const int domain_num = mesh_component.attribute_domain_num(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); source_evaluator_->add(src_field_); source_evaluator_->evaluate(); source_data_ = &source_evaluator_->get_evaluated(0); @@ -578,9 +578,9 @@ class NearestTransferFunction : public fn::MultiFunction { { if (use_mesh_) { const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_size = mesh.attribute_domain_size(domain_); + const int domain_num = mesh.attribute_domain_num(domain_); mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); + mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); mesh_evaluator_->add(src_field_); mesh_evaluator_->evaluate(); mesh_data_ = &mesh_evaluator_->get_evaluated(0); @@ -588,9 +588,9 @@ class NearestTransferFunction : public fn::MultiFunction { if (use_points_) { const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_size = points.attribute_domain_size(domain_); + const int domain_num = points.attribute_domain_num(domain_); point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); + point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); point_evaluator_->add(src_field_); point_evaluator_->evaluate(); point_data_ = &point_evaluator_->get_evaluated(0); @@ -658,9 +658,9 @@ class IndexTransferFunction : public fn::MultiFunction { if (component == nullptr) { return; } - const int domain_size = component->attribute_domain_size(domain_); + const int domain_num = component->attribute_domain_num(domain_); geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); evaluator_->add(src_field_); evaluator_->evaluate(); src_data_ = &evaluator_->get_evaluated(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index a5ca1cba28f..258c1ac3fba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -19,7 +19,7 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i { GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..e47dc22da04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -83,9 +83,9 @@ static void node_geo_exec(GeoNodeExecParams params) GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE); GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + FieldEvaluator evaluator{context, domain_num}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 378bac894e8..9a316190720 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -241,27 +241,27 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful case GEO_COMPONENT_TYPE_MESH: { const MeshComponent &mesh_component = *(const MeshComponent *)component; MeshInfo &info = this->mesh_info.emplace(); - info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE); break; } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *(const CurveComponent *)component; CurveInfo &info = this->curve_info.emplace(); - info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE); break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; PointCloudInfo &info = this->pointcloud_info.emplace(); - info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT); break; } case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.tot_instances = instances_component.instances_amount(); + info.instances_num = instances_component.instances_num(); break; } case GEO_COMPONENT_TYPE_VOLUME: { diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index cea3084a418..9aee3ddcce7 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -37,7 +37,7 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const { const SocketDeclaration &decl = - *provider_->dnode->input_by_identifier(identifier).bsocket()->declaration; + *provider_->dnode->input_by_identifier(identifier).bsocket()->runtime->declaration; const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl); if (geo_decl == nullptr) { return; @@ -119,14 +119,14 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type); - const int64_t domain_size = component.attribute_domain_size(domain); + const int64_t domain_num = component.attribute_domain_num(domain); if (default_value == nullptr) { default_value = cpp_type->default_value(); } if (found_socket == nullptr) { - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } if (found_socket->type == SOCK_STRING) { @@ -140,40 +140,40 @@ GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, /* If the attribute doesn't exist, use the default value and output an error message * (except when the field is empty, to avoid spamming error messages, and not when * the domain is empty and we don't expect an attribute anyway). */ - if (!name.empty() && component.attribute_domain_size(domain) != 0) { + if (!name.empty() && component.attribute_domain_num(domain) != 0) { this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_INT) { const int value = this->get_input<int>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } if (found_socket->type == SOCK_RGBA) { const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized( CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); - return GVArray::ForSingle(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_num, buffer); } BLI_assert(false); - return GVArray::ForSingle(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_num, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 0ab446d8b0c..098f766589d 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -19,6 +19,7 @@ #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "DNA_collection_types.h" #include "DNA_material_types.h" @@ -261,8 +262,8 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) } if (ntype->declare != nullptr) { nodeDeclarationEnsureOnOutdatedNode(ntree, node); - if (!node->declaration->matches(*node)) { - refresh_node(*ntree, *node, *node->declaration, do_id_user); + if (!node->runtime->declaration->matches(*node)) { + refresh_node(*ntree, *node, *node->runtime->declaration, do_id_user); } nodeSocketDeclarationsUpdate(node); return; diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 06925761bc7..b9fb75f30c7 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -4,6 +4,7 @@ #include "NOD_socket_declarations_geometry.hh" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BLI_math_vector.h" @@ -33,14 +34,14 @@ static bool sockets_can_connect(const SocketDeclaration &socket_decl, return false; } - if (other_socket.declaration) { + if (other_socket.runtime->declaration) { if (socket_decl.in_out() == SOCK_IN) { - if (!field_types_are_compatible(socket_decl, *other_socket.declaration)) { + if (!field_types_are_compatible(socket_decl, *other_socket.runtime->declaration)) { return false; } } else { - if (!field_types_are_compatible(*other_socket.declaration, socket_decl)) { + if (!field_types_are_compatible(*other_socket.runtime->declaration, socket_decl)) { return false; } } diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 5d2e1663ae3..e8be093c606 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -226,6 +226,39 @@ void node_filter_label(const bNodeTree *UNUSED(ntree), const bNode *node, char * BLI_strncpy(label, IFACE_(name), maxlen); } +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode) +{ + bNodeSocket *sock1 = (bNodeSocket *)sockets->first; + bNodeSocket *sock2 = sock1->next; + bNodeSocket *sock3 = sock2->next; + + node_sock_label_clear(sock1); + node_sock_label_clear(sock2); + node_sock_label_clear(sock3); + + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + node_sock_label(sock1, "Red"); + node_sock_label(sock2, "Green"); + node_sock_label(sock3, "Blue"); + break; + case NODE_COMBSEP_COLOR_HSL: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Lightness"); + break; + case NODE_COMBSEP_COLOR_HSV: + node_sock_label(sock1, "Hue"); + node_sock_label(sock2, "Saturation"); + node_sock_label(sock3, "Value"); + break; + default: { + BLI_assert_unreachable(); + break; + } + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h index de03a176c0c..0a2a7a70091 100644 --- a/source/blender/nodes/intern/node_util.h +++ b/source/blender/nodes/intern/node_util.h @@ -65,6 +65,7 @@ void node_filter_label(const struct bNodeTree *ntree, const struct bNode *node, char *label, int maxlen); +void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode); /*** Link Handling */ diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 9b4ea0e0db6..3e90f185168 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC nodes/node_shader_rgb.cc nodes/node_shader_rgb_to_bw.cc nodes/node_shader_script.cc + nodes/node_shader_sepcomb_color.cc nodes/node_shader_sepcomb_hsv.cc nodes/node_shader_sepcomb_rgb.cc nodes/node_shader_sepcomb_xyz.cc diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index ec97637ccd2..f107ec73c60 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -587,7 +587,7 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, } } } - /* Recreate links between copied nodes AND incomming links to the copied nodes. */ + /* Recreate links between copied nodes AND incoming links to the copied nodes. */ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (link->tonode->tmp_flag >= 0) { bool from_node_copied = link->fromnode->tmp_flag >= 0; diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index b1db0248d9f..eb47059063d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -28,48 +28,34 @@ static int gpu_shader_curve_vec(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[3][4]; - float range_xyz[3]; - - for (int a = 0; a < 3; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_xyz[a][0] = cm->mintable; - ext_xyz[a][2] = cm->maxtable; - range_xyz[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[a][1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_xyz[a])) : - 1e8f; - ext_xyz[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_xyz[a])) : - 1e8f; - } - else { - ext_xyz[a][1] = 0.0f; - ext_xyz[a][3] = 0.0f; - } - } + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); return GPU_stack_link(mat, node, - "curves_vec", + "curves_vector_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_xyz), - GPU_uniform(ext_xyz[0]), - GPU_uniform(ext_xyz[1]), - GPU_uniform(ext_xyz[2])); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveVecFunction : public fn::MultiFunction { @@ -157,72 +143,59 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - bool use_opti = true; - - CurveMapping *cumap = (CurveMapping *)node->storage; - - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_RGBA(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_rgba[4][4]; - float range_rgba[4]; - - for (int a = 0; a < CM_TOT; a++) { - const CurveMap *cm = &cumap->cm[a]; - ext_rgba[a][0] = cm->mintable; - ext_rgba[a][2] = cm->maxtable; - range_rgba[a] = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_rgba[a][1] = (cm->ext_in[0] != 0.0f) ? - (cm->ext_in[1] / (cm->ext_in[0] * range_rgba[a])) : - 1e8f; - ext_rgba[a][3] = (cm->ext_out[0] != 0.0f) ? - (cm->ext_out[1] / (cm->ext_out[0] * range_rgba[a])) : - 1e8f; - } - else { - ext_rgba[a][1] = 0.0f; - ext_rgba[a][3] = 0.0f; - } - - /* Check if rgb comps are just linear. */ - if (a < 3) { - if (range_rgba[a] != 1.0f || ext_rgba[a][1] != 1.0f || ext_rgba[a][2] != 1.0f || - cm->totpoint != 2 || cm->curve[0].x != 0.0f || cm->curve[0].y != 0.0f || - cm->curve[1].x != 1.0f || cm->curve[1].y != 1.0f) { - use_opti = false; - } - } - } - - if (use_opti) { + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); + + /* Shader nodes don't do white balancing. */ + float black_level[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + float white_level[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + /* If the RGB curves do nothing, use a function that skips RGB computations. */ + if (BKE_curvemapping_is_map_identity(curve_mapping, 0) && + BKE_curvemapping_is_map_identity(curve_mapping, 1) && + BKE_curvemapping_is_map_identity(curve_mapping, 2)) { return GPU_stack_link(mat, node, - "curves_rgb_opti", + "curves_combined_only", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(&range_minimums[3]), + GPU_uniform(&range_dividers[3]), + GPU_uniform(&start_slopes[3]), + GPU_uniform(&end_slopes[3])); } return GPU_stack_link(mat, node, - "curves_rgb", + "curves_combined_rgb", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(range_rgba), - GPU_uniform(ext_rgba[0]), - GPU_uniform(ext_rgba[1]), - GPU_uniform(ext_rgba[2]), - GPU_uniform(ext_rgba[3])); + GPU_constant(black_level), + GPU_constant(white_level), + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveRGBFunction : public fn::MultiFunction { @@ -316,40 +289,34 @@ static int gpu_shader_curve_float(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - float *array, layer; - int size; - - CurveMapping *cumap = (CurveMapping *)node->storage; + CurveMapping *curve_mapping = (CurveMapping *)node->storage; + + BKE_curvemapping_init(curve_mapping); + float *band_values; + int band_size; + BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size); + float band_layer; + GPUNodeLink *band_texture = GPU_color_band(mat, band_size, band_values, &band_layer); + + float start_slopes[CM_TOT]; + float end_slopes[CM_TOT]; + BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes); + float range_minimums[CM_TOT]; + BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums); + float range_dividers[CM_TOT]; + BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers); - BKE_curvemapping_init(cumap); - BKE_curvemapping_table_F(cumap, &array, &size); - GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); - - float ext_xyz[4]; - float range_x; - - const CurveMap *cm = &cumap->cm[0]; - ext_xyz[0] = cm->mintable; - ext_xyz[2] = cm->maxtable; - range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); - /* Compute extrapolation gradients. */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { - ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; - ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; - } - else { - ext_xyz[1] = 0.0f; - ext_xyz[3] = 0.0f; - } return GPU_stack_link(mat, node, - "curve_float", + "curves_float_mixed", in, out, - tex, - GPU_constant(&layer), - GPU_uniform(&range_x), - GPU_uniform(ext_xyz)); + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); } class CurveFloatFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc new file mode 100644 index 00000000000..8e378ebabce --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_color.cc @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static void node_combsep_color_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__); + data->mode = NODE_COMBSEP_COLOR_RGB; + node->storage = data; +} + +/* **************** SEPARATE COLOR ******************** */ + +namespace blender::nodes::node_shader_separate_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_sepcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Float>(N_("Red")); + b.add_output<decl::Float>(N_("Green")); + b.add_output<decl::Float>(N_("Blue")); +} + +static void node_sepcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "separate_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "separate_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "separate_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_sepcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_separate_color_cc + +void register_node_type_sh_sepcolor() +{ + namespace file_ns = blender::nodes::node_shader_separate_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_sepcolor_declare; + node_type_update(&ntype, file_ns::node_sepcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_sepcolor); + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE COLOR ******************** */ + +namespace blender::nodes::node_shader_combine_color_cc { + +NODE_STORAGE_FUNCS(NodeCombSepColor) + +static void sh_node_combcolor_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Green")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Blue")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); +} + +static void node_combcolor_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeCombSepColor &storage = node_storage(*node); + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_COMBSEP_COLOR_RGB: + return "combine_color_rgb"; + case NODE_COMBSEP_COLOR_HSV: + return "combine_color_hsv"; + case NODE_COMBSEP_COLOR_HSL: + return "combine_color_hsl"; + } + + return nullptr; +} + +static int gpu_shader_combcolor(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeCombSepColor &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.mode); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); + } + + return 0; +} + +} // namespace blender::nodes::node_shader_combine_color_cc + +void register_node_type_sh_combcolor() +{ + namespace file_ns = blender::nodes::node_shader_combine_color_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_combcolor_declare; + node_type_update(&ntype, file_ns::node_combcolor_update); + node_type_init(&ntype, node_combsep_color_init); + node_type_storage( + &ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_combcolor); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 129c8cf4b97..6dfabe48292 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -36,7 +36,7 @@ void register_node_type_sh_sephsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_sephsv; node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); @@ -72,7 +72,7 @@ void register_node_type_sh_combhsv() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER); + sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_combhsv; node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 657f591a50c..28b55047633 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -76,7 +76,7 @@ void register_node_type_sh_seprgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB_LEGACY, "Separate RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_seprgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_seprgb); ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; @@ -119,7 +119,7 @@ void register_node_type_sh_combrgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER); + sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB_LEGACY, "Combine RGB", NODE_CLASS_CONVERTER); ntype.declare = file_ns::sh_node_combrgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_combrgb); ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index 06c238fead0..c9588949761 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -74,7 +74,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, const char *gpu_node_name = use_cubic ? "node_tex_tile_cubic" : "node_tex_tile_linear"; GPUNodeLink *gpu_image = GPU_image_tiled(mat, ima, iuser, sampler_state); GPUNodeLink *gpu_image_tile_mapping = GPU_image_tiled_mapping(mat, ima, iuser); - /* UDIM tiles needs a samper2DArray and sampler1DArray for tile mapping. */ + /* UDIM tiles needs a `sampler2DArray` and `sampler1DArray` for tile mapping. */ GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image, gpu_image_tile_mapping); } else { diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 7706f118507..5bed54ebfd7 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRC nodes/node_texture_at.c nodes/node_texture_bricks.c nodes/node_texture_checker.c + nodes/node_texture_combine_color.c nodes/node_texture_common.c nodes/node_texture_compose.c nodes/node_texture_coord.c @@ -39,6 +40,7 @@ set(SRC nodes/node_texture_proc.c nodes/node_texture_rotate.c nodes/node_texture_scale.c + nodes/node_texture_separate_color.c nodes/node_texture_texture.c nodes/node_texture_translate.c nodes/node_texture_valToNor.c diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 76208104a8c..248114f242a 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -49,7 +49,7 @@ static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, sh } } -static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, short thread) +static void tex_input(float *out, int num, bNodeStack *in, TexParams *params, short thread) { TexDelegate *dg = in->data; if (dg) { @@ -59,7 +59,7 @@ static void tex_input(float *out, int sz, bNodeStack *in, TexParams *params, sho in->vec[1] = in->vec[2] = in->vec[0]; } } - memcpy(out, in->vec, sz * sizeof(float)); + memcpy(out, in->vec, num * sizeof(float)); } void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread) diff --git a/source/blender/nodes/texture/nodes/node_texture_combine_color.c b/source/blender/nodes/texture/nodes/node_texture_combine_color.c new file mode 100644 index 00000000000..459553bc950 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_combine_color.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" + +static bNodeSocketTemplate inputs[] = { + {SOCK_FLOAT, N_("Red"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Green"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Blue"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_RGBA, N_("Color")}, + {-1, ""}, +}; + +static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + int i; + for (i = 0; i < 4; i++) { + out[i] = tex_input_value(in[i], p, thread); + } + /* Apply color space if required. */ + switch (node->custom1) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + hsv_to_rgb_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + hsl_to_rgb_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &colorfn, data); +} + +void register_node_type_tex_combine_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c index e341b65ac97..ef14062c72d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_compose.c +++ b/source/blender/nodes/texture/nodes/node_texture_compose.c @@ -42,7 +42,7 @@ void register_node_type_tex_compose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_COMPOSE_LEGACY, "Combine RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c index 21c3944e255..2d42fa4602e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_decompose.c +++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c @@ -62,7 +62,7 @@ void register_node_type_tex_decompose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR); + tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE_LEGACY, "Separate RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_separate_color.c b/source/blender/nodes/texture/nodes/node_texture_separate_color.c new file mode 100644 index 00000000000..a482a3f3421 --- /dev/null +++ b/source/blender/nodes/texture/nodes/node_texture_separate_color.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup texnodes + */ + +#include "BLI_listbase.h" +#include "NOD_texture.h" +#include "node_texture_util.h" +#include <math.h> + +static bNodeSocketTemplate inputs[] = { + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, + {-1, ""}, +}; +static bNodeSocketTemplate outputs[] = { + {SOCK_FLOAT, N_("Red")}, + {SOCK_FLOAT, N_("Green")}, + {SOCK_FLOAT, N_("Blue")}, + {SOCK_FLOAT, N_("Alpha")}, + {-1, ""}, +}; + +static void apply_color_space(float *out, NodeCombSepColorMode type) +{ + switch (type) { + case NODE_COMBSEP_COLOR_RGB: { + /* Pass */ + break; + } + case NODE_COMBSEP_COLOR_HSV: { + rgb_to_hsv_v(out, out); + break; + } + case NODE_COMBSEP_COLOR_HSL: { + rgb_to_hsl_v(out, out); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +static void valuefn_r(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[0]; +} + +static void valuefn_g(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[1]; +} + +static void valuefn_b(float *out, TexParams *p, bNode *node, bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + apply_color_space(out, (NodeCombSepColorMode)node->custom1); + *out = out[2]; +} + +static void valuefn_a(float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread) +{ + tex_input_rgba(out, in[0], p, thread); + *out = out[3]; +} + +static void update(bNodeTree *UNUSED(ntree), bNode *node) +{ + node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)node->custom1); +} + +static void exec(void *data, + int UNUSED(thread), + bNode *node, + bNodeExecData *execdata, + bNodeStack **in, + bNodeStack **out) +{ + tex_output(node, execdata, in, out[0], &valuefn_r, data); + tex_output(node, execdata, in, out[1], &valuefn_g, data); + tex_output(node, execdata, in, out[2], &valuefn_b, data); + tex_output(node, execdata, in, out[3], &valuefn_a, data); +} + +void register_node_type_tex_separate_color(void) +{ + static bNodeType ntype; + + tex_node_type_base(&ntype, TEX_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_OP_COLOR); + node_type_socket_templates(&ntype, inputs, outputs); + node_type_exec(&ntype, NULL, NULL, exec); + node_type_update(&ntype, update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index baa2b0deb71..972a782d650 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1259,10 +1259,16 @@ static PyObject *bpy_bmesh_select_flush(BPy_BMesh *self, PyObject *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmesh_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update mesh normals.\n"); +PyDoc_STRVAR( + bpy_bmesh_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of mesh faces and verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of any vertex where :attr:`is_wire` is True will be a zero vector.\n"); + static PyObject *bpy_bmesh_normal_update(BPy_BMesh *self) { BPY_BM_CHECK_OBJ(self); @@ -1611,7 +1617,12 @@ static PyObject *bpy_bmvert_calc_shell_factor(BPy_BMVert *self) PyDoc_STRVAR(bpy_bmvert_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update vertex normal.\n"); + " Update vertex normal.\n" + " This does not update the normals of adjoining faces.\n" + "\n" + " .. note::\n" + "\n" + " The vertex normal will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmvert_normal_update(BPy_BMVert *self) { BPY_BM_CHECK_OBJ(self); @@ -1773,10 +1784,15 @@ static PyObject *bpy_bmedge_other_vert(BPy_BMEdge *self, BPy_BMVert *value) Py_RETURN_NONE; } -PyDoc_STRVAR(bpy_bmedge_normal_update_doc, - ".. method:: normal_update()\n" - "\n" - " Update edges vertex normals.\n"); +PyDoc_STRVAR( + bpy_bmedge_normal_update_doc, + ".. method:: normal_update()\n" + "\n" + " Update normals of all connected faces and the edge verts.\n" + "\n" + " .. note::\n" + "\n" + " The normal of edge vertex will be a zero vector if vertex :attr:`is_wire` is True.\n"); static PyObject *bpy_bmedge_normal_update(BPy_BMEdge *self) { BPY_BM_CHECK_OBJ(self); @@ -2012,7 +2028,8 @@ static PyObject *bpy_bmface_calc_center_bounds(BPy_BMFace *self) PyDoc_STRVAR(bpy_bmface_normal_update_doc, ".. method:: normal_update()\n" "\n" - " Update face's normal.\n"); + " Update face normal based on the positions of the face verts.\n" + " This does not update the normals of face verts.\n"); static PyObject *bpy_bmface_normal_update(BPy_BMFace *self) { BPY_BM_CHECK_OBJ(self); diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 9e45105d105..060b7758ea9 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -50,7 +50,7 @@ static PyObject *py_blf_position(PyObject *UNUSED(self), PyObject *args) PyDoc_STRVAR(py_blf_size_doc, ".. function:: size(fontid, size, dpi)\n" "\n" - " Set the size and dpi for drawing text.\n" + " Set the size and DPI for drawing text.\n" "\n" " :arg fontid: The id of the typeface as returned by :func:`blf.load`, for default " "font use 0.\n" diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 2fbb6b8ee05..3f880da2f56 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1279,9 +1279,8 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args) pyform = BPy_IDGroup_MapDataToPy(idprop); if (pyform == NULL) { - /* ok something bad happened with the #PyObject, - * so don't remove the prop from the group. if `pyform is - * NULL, then it already should have raised an exception. */ + /* Ok something bad happened with the #PyObject, so don't remove the prop from the group. + * if `pyform` is NULL, then it already should have raised an exception. */ return NULL; } diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt index e726cb7883d..8ccb29beb13 100644 --- a/source/blender/python/gpu/CMakeLists.txt +++ b/source/blender/python/gpu/CMakeLists.txt @@ -29,8 +29,8 @@ set(SRC gpu_py_offscreen.c gpu_py_platform.c gpu_py_select.c - gpu_py_shader_create_info.cc gpu_py_shader.c + gpu_py_shader_create_info.cc gpu_py_state.c gpu_py_texture.c gpu_py_types.c diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 77333c6dfea..d7369731a98 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -46,6 +46,9 @@ "``3D_FLAT_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ + "``3D_IMAGE``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image\n" \ "``3D_SMOOTH_COLOR``\n" \ " :Attributes: vec3 pos, vec4 color\n" \ " :Uniforms: none\n" \ @@ -68,6 +71,7 @@ static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, + {GPU_SHADER_3D_IMAGE, "3D_IMAGE"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, @@ -748,28 +752,27 @@ static PyObject *pygpu_shader_unbind(BPyGPUShader *UNUSED(self)) Py_RETURN_NONE; } -PyDoc_STRVAR(pygpu_shader_from_builtin_doc, - ".. function:: from_builtin(shader_name, config='DEFAULT')\n" - "\n" - " Shaders that are embedded in the blender internal code:\n" - "" PYDOC_BUILTIN_SHADER_DESCRIPTION - "\n" - " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n" - " which can be edited by the :mod:`gpu.matrix` module.\n" - "\n" - " You can also choose a shader configuration that uses clip_planes by setting the " - "``CLIPPED`` value to the config parameter. Note that in this case you also need to " - "manually set the value of ``mat4 ModelMatrix``.\n" - "\n" - " :param shader_name: One of the builtin shader names.\n" - " :type shader_name: str\n" - " :param config: One of these types of shader configuration:\n" - "\n" - " - ``DEFAULT``\n" - " - ``CLIPPED``\n" - " :type config: str\n" - " :return: Shader object corresponding to the given name.\n" - " :rtype: :class:`bpy.types.GPUShader`\n"); +PyDoc_STRVAR( + pygpu_shader_from_builtin_doc, + ".. function:: from_builtin(shader_name, config='DEFAULT')\n" + "\n" + " Shaders that are embedded in the blender internal code (see :ref:`built-in-shaders`).\n" + " They all read the uniform ``mat4 ModelViewProjectionMatrix``,\n" + " which can be edited by the :mod:`gpu.matrix` module.\n" + "\n" + " You can also choose a shader configuration that uses clip_planes by setting the " + "``CLIPPED`` value to the config parameter. Note that in this case you also need to " + "manually set the value of ``mat4 ModelMatrix``.\n" + "\n" + " :param shader_name: One of the builtin shader names.\n" + " :type shader_name: str\n" + " :param config: One of these types of shader configuration:\n" + "\n" + " - ``DEFAULT``\n" + " - ``CLIPPED``\n" + " :type config: str\n" + " :return: Shader object corresponding to the given name.\n" + " :rtype: :class:`bpy.types.GPUShader`\n"); static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) { BPYGPU_IS_INIT_OR_ERROR_OBJ; @@ -850,6 +853,8 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = { PyDoc_STRVAR(pygpu_shader_module__tp_doc, "This module provides access to GPUShader internal functions.\n" "\n" + ".. _built-in-shaders:\n" + "\n" ".. rubric:: Built-in shaders\n" "\n" "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n" diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index e4e198ab812..8179daa8e03 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -143,10 +143,13 @@ if(WITH_PYTHON_SAFETY) add_definitions(-DWITH_PYTHON_SAFETY) endif() - - if(WITH_AUDASPACE) + # It's possible to build with AUDASPACE (for file IO) but without the `aud` Python API, + # when building without NUMPY so define both `WITH_AUDASPACE` & `DWITH_AUDASPACE_PY`. add_definitions(-DWITH_AUDASPACE) + if(WITH_PYTHON_NUMPY) + add_definitions(-DWITH_AUDASPACE_PY) + endif() endif() if(WITH_BULLET) @@ -229,7 +232,7 @@ if(WITH_IMAGE_TIFF) endif() if(WITH_WEBP) - add_definitions(-DWITH_WEBP) + add_definitions(-DWITH_WEBP) endif() if(WITH_INPUT_NDOF) diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 748a501ef76..2e97ae0fc1d 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -23,6 +23,7 @@ #include "BKE_global.h" /* XXX, G_MAIN only */ #include "RNA_access.h" +#include "RNA_enum_types.h" #include "RNA_prototypes.h" #include "RNA_types.h" @@ -400,6 +401,97 @@ static PyObject *bpy_unescape_identifier(PyObject *UNUSED(self), PyObject *value return value_unescape; } +/** + * \note only exposed for generating documentation, see: `doc/python_api/sphinx_doc_gen.py`. + */ +PyDoc_STRVAR( + bpy_context_members_doc, + ".. function:: context_members()\n" + "\n" + " :return: A dict where the key is the context and the value is a tuple of it's members.\n" + " :rtype: dict\n"); +static PyObject *bpy_context_members(PyObject *UNUSED(self)) +{ + extern const char *buttons_context_dir[]; + extern const char *clip_context_dir[]; + extern const char *file_context_dir[]; + extern const char *image_context_dir[]; + extern const char *node_context_dir[]; + extern const char *screen_context_dir[]; + extern const char *sequencer_context_dir[]; + extern const char *text_context_dir[]; + extern const char *view3d_context_dir[]; + + struct { + const char *name; + const char **dir; + } context_members_all[] = { + {"buttons", buttons_context_dir}, + {"clip", clip_context_dir}, + {"file", file_context_dir}, + {"image", image_context_dir}, + {"node", node_context_dir}, + {"screen", screen_context_dir}, + {"sequencer", sequencer_context_dir}, + {"text", text_context_dir}, + {"view3d", view3d_context_dir}, + }; + + PyObject *result = _PyDict_NewPresized(ARRAY_SIZE(context_members_all)); + for (int context_index = 0; context_index < ARRAY_SIZE(context_members_all); context_index++) { + const char *name = context_members_all[context_index].name; + const char **dir = context_members_all[context_index].dir; + int i; + for (i = 0; dir[i]; i++) { + /* Pass. */ + } + PyObject *members = PyTuple_New(i); + for (i = 0; dir[i]; i++) { + PyTuple_SET_ITEM(members, i, PyUnicode_FromString(dir[i])); + } + PyDict_SetItemString(result, name, members); + Py_DECREF(members); + } + BLI_assert(PyDict_GET_SIZE(result) == ARRAY_SIZE(context_members_all)); + + return result; +} + +/** + * \note only exposed for generating documentation, see: `doc/python_api/sphinx_doc_gen.py`. + */ +PyDoc_STRVAR(bpy_rna_enum_items_static_doc, + ".. function:: rna_enum_items_static()\n" + "\n" + " :return: A dict where the key the name of the enum, the value is a tuple of " + ":class:`bpy.types.EnumPropertyItem`.\n" + " :rtype: dict of \n"); +static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self)) +{ +#define DEF_ENUM(id) {STRINGIFY(id), id}, + struct { + const char *id; + const EnumPropertyItem *items; + } enum_info[] = { +#include "RNA_enum_items.h" + }; + PyObject *result = _PyDict_NewPresized(ARRAY_SIZE(enum_info)); + for (int i = 0; i < ARRAY_SIZE(enum_info); i++) { + /* Include all items (including headings & separators), can be shown in documentation. */ + const EnumPropertyItem *items = enum_info[i].items; + const int items_count = RNA_enum_items_count(items); + PyObject *value = PyTuple_New(items_count); + for (int item_index = 0; item_index < items_count; item_index++) { + PointerRNA ptr; + RNA_pointer_create(NULL, &RNA_EnumPropertyItem, (void *)&items[item_index], &ptr); + PyTuple_SET_ITEM(value, item_index, pyrna_struct_CreatePyObject(&ptr)); + } + PyDict_SetItemString(result, enum_info[i].id, value); + Py_DECREF(value); + } + return result; +} + static PyMethodDef meth_bpy_script_paths = { "script_paths", (PyCFunction)bpy_script_paths, @@ -448,6 +540,18 @@ static PyMethodDef meth_bpy_unescape_identifier = { METH_O, bpy_unescape_identifier_doc, }; +static PyMethodDef meth_bpy_context_members = { + "context_members", + (PyCFunction)bpy_context_members, + METH_NOARGS, + bpy_context_members_doc, +}; +static PyMethodDef meth_bpy_rna_enum_items_static = { + "rna_enum_items_static", + (PyCFunction)bpy_rna_enum_items_static, + METH_NOARGS, + bpy_rna_enum_items_static_doc, +}; static PyObject *bpy_import_test(const char *modname) { @@ -551,6 +655,12 @@ void BPy_init_modules(struct bContext *C) (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); PyModule_AddObject( mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); + PyModule_AddObject(mod, + meth_bpy_context_members.ml_name, + (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL)); + PyModule_AddObject(mod, + meth_bpy_rna_enum_items_static.ml_name, + (PyObject *)PyCFunction_New(&meth_bpy_rna_enum_items_static, NULL)); /* register funcs (bpy_rna.c) */ PyModule_AddObject(mod, diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index e05cfb771c1..0ab8b4385e5 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -238,7 +238,7 @@ void BPY_context_set(bContext *C) extern PyObject *Manta_initPython(void); #endif -#ifdef WITH_AUDASPACE +#ifdef WITH_AUDASPACE_PY /* defined in AUD_C-API.cpp */ extern PyObject *AUD_initPython(void); #endif @@ -272,7 +272,7 @@ static struct _inittab bpy_internal_modules[] = { #ifdef WITH_FLUID {"manta", Manta_initPython}, #endif -#ifdef WITH_AUDASPACE +#ifdef WITH_AUDASPACE_PY {"aud", AUD_initPython}, #endif #ifdef WITH_CYCLES @@ -312,6 +312,14 @@ void BPY_python_start(bContext *C, int argc, const char **argv) PyPreConfig preconfig; PyStatus status; + /* To narrow down reports where the systems Python is inexplicably used, see: T98131. */ + CLOG_INFO( + BPY_LOG_INTERFACE, + 2, + "Initializing %s support for the systems Python environment such as 'PYTHONPATH' and " + "the user-site directory.", + py_use_system_env ? "*with*" : "*without*"); + if (py_use_system_env) { PyPreConfig_InitPythonConfig(&preconfig); } @@ -579,9 +587,9 @@ void BPY_python_backtrace(FILE *fp) PyFrameObject *frame = tstate->frame; do { const int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); - const char *filename = PyUnicode_AsUTF8(frame->f_code->co_filename); + const char *filepath = PyUnicode_AsUTF8(frame->f_code->co_filename); const char *funcname = PyUnicode_AsUTF8(frame->f_code->co_name); - fprintf(fp, " File \"%s\", line %d in %s\n", filename, line, funcname); + fprintf(fp, " File \"%s\", line %d in %s\n", filepath, line, funcname); } while ((frame = frame->f_back)); } } @@ -770,16 +778,16 @@ static void bpy_module_delay_init(PyObject *bpy_proxy) const char *argv[2]; /* updating the module dict below will lose the reference to __file__ */ - PyObject *filename_obj = PyModule_GetFilenameObject(bpy_proxy); + PyObject *filepath_obj = PyModule_GetFilenameObject(bpy_proxy); - const char *filename_rel = PyUnicode_AsUTF8(filename_obj); /* can be relative */ - char filename_abs[1024]; + const char *filepath_rel = PyUnicode_AsUTF8(filepath_obj); /* can be relative */ + char filepath_abs[1024]; - BLI_strncpy(filename_abs, filename_rel, sizeof(filename_abs)); - BLI_path_abs_from_cwd(filename_abs, sizeof(filename_abs)); - Py_DECREF(filename_obj); + BLI_strncpy(filepath_abs, filepath_rel, sizeof(filepath_abs)); + BLI_path_abs_from_cwd(filepath_abs, sizeof(filepath_abs)); + Py_DECREF(filepath_obj); - argv[0] = filename_abs; + argv[0] = filepath_abs; argv[1] = NULL; // printf("module found %s\n", argv[0]); @@ -810,16 +818,16 @@ PyMODINIT_FUNC PyInit_bpy(void) PyObject *bpy_proxy = PyModule_Create(&bpy_proxy_def); /* Problem: - * 1) this init function is expected to have a private member defined - 'md_def' + * 1) this init function is expected to have a private member defined - `md_def` * but this is only set for C defined modules (not py packages) * so we can't return 'bpy_package_py' as is. * * 2) there is a 'bpy' C module for python to load which is basically all of blender, - * and there is scripts/bpy/__init__.py, + * and there is `scripts/bpy/__init__.py`, * we may end up having to rename this module so there is no naming conflict here eg: * 'from blender import bpy' * - * 3) we don't know the filename at this point, workaround by assigning a dummy value + * 3) we don't know the filepath at this point, workaround by assigning a dummy value * which calls back when its freed so the real loading can take place. */ diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index e0e5dd869ba..9754599eeed 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -182,7 +182,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k Main *bmain_base = CTX_data_main(BPY_context_get()); Main *bmain = self->ptr.data; /* Typically #G_MAIN */ BPy_Library *ret; - const char *filename = NULL; + const char *filepath = NULL; bool is_rel = false, is_link = false, use_assets_only = false; static const char *_keywords[] = {"filepath", "link", "relative", "assets_only", NULL}; @@ -200,7 +200,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, - &filename, + &filepath, PyC_ParseBool, &is_link, PyC_ParseBool, @@ -212,8 +212,8 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k ret = PyObject_New(BPy_Library, &bpy_lib_Type); - BLI_strncpy(ret->relpath, filename, sizeof(ret->relpath)); - BLI_strncpy(ret->abspath, filename, sizeof(ret->abspath)); + BLI_strncpy(ret->relpath, filepath, sizeof(ret->relpath)); + BLI_strncpy(ret->abspath, filepath, sizeof(ret->abspath)); BLI_path_abs(ret->abspath, BKE_main_blendfile_path(bmain)); ret->bmain = bmain; diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index a6aa1f46b0c..3f2154189e8 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -45,150 +45,32 @@ /** \name Shared Enums & Doc-Strings * \{ */ -static const EnumPropertyItem property_flag_items[] = { - {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, - {PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", ""}, - {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", ""}, - {PROP_LIB_EXCEPTION, "LIBRARY_EDITABLE", 0, "Library Editable", ""}, - {PROP_PROPORTIONAL, "PROPORTIONAL", 0, "Adjust values proportionally to eachother", ""}, - {PROP_TEXTEDIT_UPDATE, - "TEXTEDIT_UPDATE", - 0, - "Update on every keystroke in textedit 'mode'", - ""}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_OPTIONS_DOC \ - " :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', " \ - "'PROPORTIONAL'," \ - "'TEXTEDIT_UPDATE'].\n" \ + " :arg options: Enumerator in :ref:`rna_enum_property_flag_items`.\n" \ " :type options: set\n" -static const EnumPropertyItem property_flag_enum_items[] = { - {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, - {PROP_SKIP_SAVE, "SKIP_SAVE", 0, "Skip Save", ""}, - {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animatable", ""}, - {PROP_LIB_EXCEPTION, "LIBRARY_EDITABLE", 0, "Library Editable", ""}, - {PROP_ENUM_FLAG, "ENUM_FLAG", 0, "Enum Flag", ""}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_OPTIONS_ENUM_DOC \ - " :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'ENUM_FLAG', " \ - "'LIBRARY_EDITABLE'].\n" \ + " :arg options: Enumerator in :ref:`rna_enum_property_flag_enum_items`.\n" \ " :type options: set\n" -static const EnumPropertyItem property_flag_override_items[] = { - {PROPOVERRIDE_OVERRIDABLE_LIBRARY, - "LIBRARY_OVERRIDABLE", - 0, - "Library Overridable", - "Make that property editable in library overrides of linked data-blocks"}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_OPTIONS_OVERRIDE_DOC \ - " :arg override: Enumerator in ['LIBRARY_OVERRIDABLE'].\n" \ + " :arg override: Enumerator in :ref:`rna_enum_property_override_flag_items`.\n" \ " :type override: set\n" -static const EnumPropertyItem property_flag_override_collection_items[] = { - {PROPOVERRIDE_OVERRIDABLE_LIBRARY, - "LIBRARY_OVERRIDABLE", - 0, - "Library Overridable", - "Make that property editable in library overrides of linked data-blocks"}, - {PROPOVERRIDE_NO_PROP_NAME, - "NO_PROPERTY_NAME", - 0, - "No Name", - "Do not use the names of the items, only their indices in the collection"}, - {PROPOVERRIDE_LIBRARY_INSERTION, - "USE_INSERTION", - 0, - "Use Insertion", - "Allow users to add new items in that collection in library overrides"}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC \ - " :arg override: Enumerator in ['LIBRARY_OVERRIDABLE', 'NO_PROPERTY_NAME', " \ - "'USE_INSERTION'].\n" \ + " :arg override: Enumerator in :ref:`rna_enum_property_override_flag_collection_items`.\n" \ " :type override: set\n" -/* subtypes */ -/* Keep in sync with RNA_types.h PropertySubType and rna_rna.c's rna_enum_property_subtype_items */ -static const EnumPropertyItem property_subtype_string_items[] = { - {PROP_FILEPATH, "FILE_PATH", 0, "File Path", ""}, - {PROP_DIRPATH, "DIR_PATH", 0, "Directory Path", ""}, - {PROP_FILENAME, "FILE_NAME", 0, "Filename", ""}, - {PROP_BYTESTRING, "BYTE_STRING", 0, "Byte String", ""}, - {PROP_PASSWORD, "PASSWORD", 0, "Password", "A string that is displayed hidden ('********')"}, - - {PROP_NONE, "NONE", 0, "None", ""}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_SUBTYPE_STRING_DOC \ - " :arg subtype: Enumerator in ['FILE_PATH', 'DIR_PATH', 'FILE_NAME', 'BYTE_STRING', " \ - "'PASSWORD', 'NONE'].\n" \ + " :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_string_items`.\n" \ " :type subtype: string\n" -static const EnumPropertyItem property_subtype_number_items[] = { - {PROP_PIXEL, "PIXEL", 0, "Pixel", ""}, - {PROP_UNSIGNED, "UNSIGNED", 0, "Unsigned", ""}, - {PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""}, - {PROP_FACTOR, "FACTOR", 0, "Factor", ""}, - {PROP_ANGLE, "ANGLE", 0, "Angle", ""}, - {PROP_TIME, - "TIME", - 0, - "Time (Scene Relative)", - "Time specified in frames, converted to seconds based on scene frame rate"}, - {PROP_TIME_ABSOLUTE, - "TIME_ABSOLUTE", - 0, - "Time (Absolute)", - "Time specified in seconds, independent of the scene"}, - {PROP_DISTANCE, "DISTANCE", 0, "Distance", ""}, - {PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""}, - {PROP_POWER, "POWER", 0, "Power", ""}, - {PROP_TEMPERATURE, "TEMPERATURE", 0, "Temperature", ""}, - - {PROP_NONE, "NONE", 0, "None", ""}, - {0, NULL, 0, NULL, NULL}, -}; - #define BPY_PROPDEF_SUBTYPE_NUMBER_DOC \ - " :arg subtype: Enumerator in ['PIXEL', 'UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', " \ - "'TIME', 'DISTANCE', 'DISTANCE_CAMERA', 'POWER', 'TEMPERATURE', 'NONE'].\n" \ + " :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_number_items`.\n" \ " :type subtype: string\n" -static const EnumPropertyItem property_subtype_array_items[] = { - {PROP_COLOR, "COLOR", 0, "Color", ""}, - {PROP_TRANSLATION, "TRANSLATION", 0, "Translation", ""}, - {PROP_DIRECTION, "DIRECTION", 0, "Direction", ""}, - {PROP_VELOCITY, "VELOCITY", 0, "Velocity", ""}, - {PROP_ACCELERATION, "ACCELERATION", 0, "Acceleration", ""}, - {PROP_MATRIX, "MATRIX", 0, "Matrix", ""}, - {PROP_EULER, "EULER", 0, "Euler", ""}, - {PROP_QUATERNION, "QUATERNION", 0, "Quaternion", ""}, - {PROP_AXISANGLE, "AXISANGLE", 0, "Axis Angle", ""}, - {PROP_XYZ, "XYZ", 0, "XYZ", ""}, - {PROP_XYZ_LENGTH, "XYZ_LENGTH", 0, "XYZ Length", ""}, - {PROP_COLOR_GAMMA, "COLOR_GAMMA", 0, "Color Gamma", ""}, - {PROP_COORDS, "COORDINATES", 0, "Vector Coordinates", ""}, - {PROP_LAYER, "LAYER", 0, "Layer", ""}, - {PROP_LAYER_MEMBER, "LAYER_MEMBER", 0, "Layer Member", ""}, - - {PROP_NONE, "NONE", 0, "None", ""}, - {0, NULL, 0, NULL, NULL}, -}; - -#define BPY_PROPDEF_SUBTYPE_ARRAY_DOC \ - " :arg subtype: Enumerator in ['COLOR', 'TRANSLATION', 'DIRECTION', " \ - "'VELOCITY', 'ACCELERATION', 'MATRIX', 'EULER', 'QUATERNION', 'AXISANGLE', " \ - "'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \ +#define BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC \ + " :arg subtype: Enumerator in :ref:`rna_enum_property_subtype_number_array_items`.\n" \ " :type subtype: string\n" /** \} */ @@ -257,6 +139,11 @@ struct BPyPropStore { /** Wrap: #RNA_def_property_poll_runtime */ PyObject *poll_fn; } pointer_data; + /** #PROP_STRING type. */ + struct { + /** Wrap: #RNA_def_property_string_search_func_runtime */ + PyObject *search_fn; + } string_data; }; } py_data; }; @@ -1672,6 +1559,163 @@ static void bpy_prop_string_set_fn(struct PointerRNA *ptr, } } +static bool bpy_prop_string_visit_fn_call(PyObject *py_func, + PyObject *item, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + const char *text; + const char *info = NULL; + + if (PyTuple_CheckExact(item)) { + /* Positional only. */ + static const char *_keywords[] = { + "", + "", + NULL, + }; + static _PyArg_Parser _parser = { + "s" /* `text` */ + "s" /* `info` */ + ":search", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(item, NULL, &_parser, &text, &info)) { + PyC_Err_PrintWithFunc(py_func); + return false; + } + } + else { + text = PyUnicode_AsUTF8(item); + if (UNLIKELY(text == NULL)) { + PyErr_Clear(); + PyErr_Format(PyExc_TypeError, + "expected sequence of strings or tuple pairs of strings, not %.200s", + Py_TYPE(item)->tp_name); + PyC_Err_PrintWithFunc(py_func); + return false; + } + } + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = text; + visit_params.info = info; + visit_fn(visit_user_data, &visit_params); + return true; +} + +static void bpy_prop_string_visit_for_search_fn(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + struct BPyPropStore *prop_store = RNA_property_py_data_get(prop); + PyObject *py_func; + PyObject *args; + PyObject *self; + PyObject *ret; + PyGILState_STATE gilstate; + PyObject *py_edit_text; + + BLI_assert(prop_store != NULL); + + if (C) { + bpy_context_set((struct bContext *)C, &gilstate); + } + else { + gilstate = PyGILState_Ensure(); + } + + py_func = prop_store->py_data.string_data.search_fn; + + args = PyTuple_New(3); + self = pyrna_struct_as_instance(ptr); + PyTuple_SET_ITEM(args, 0, self); + + Py_INCREF(bpy_context_module); + PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module); + + py_edit_text = PyUnicode_FromString(edit_text); + PyTuple_SET_ITEM(args, 2, py_edit_text); + + ret = PyObject_CallObject(py_func, args); + + Py_DECREF(args); + + if (ret == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + if (PyIter_Check(ret)) { + /* Iterators / generator types. */ + PyObject *it; + PyObject *(*iternext)(PyObject *); + it = PyObject_GetIter(ret); + if (it == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + iternext = *Py_TYPE(it)->tp_iternext; + for (;;) { + PyObject *py_text = iternext(it); + if (py_text == NULL) { + break; + } + const bool ok = bpy_prop_string_visit_fn_call( + py_func, py_text, visit_fn, visit_user_data); + Py_DECREF(py_text); + if (!ok) { + break; + } + } + Py_DECREF(it); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + } + else { + PyC_Err_PrintWithFunc(py_func); + } + } + } + } + else { + /* Sequence (typically list/tuple). */ + PyObject *ret_fast = PySequence_Fast( + ret, + "StringProperty(...): " + "return value from search callback was not a sequence, iterator or generator"); + if (ret_fast == NULL) { + PyC_Err_PrintWithFunc(py_func); + } + else { + const Py_ssize_t ret_num = PySequence_Fast_GET_SIZE(ret_fast); + PyObject **ret_fast_items = PySequence_Fast_ITEMS(ret_fast); + for (Py_ssize_t i = 0; i < ret_num; i++) { + const bool ok = bpy_prop_string_visit_fn_call( + py_func, ret_fast_items[i], visit_fn, visit_user_data); + if (!ok) { + break; + } + } + Py_DECREF(ret_fast); + } + } + + Py_DECREF(ret); + } + + if (C) { + bpy_context_clear((struct bContext *)C, &gilstate); + } + else { + PyGILState_Release(gilstate); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2352,11 +2396,14 @@ static void bpy_prop_callback_assign_float_array(struct PropertyRNA *prop, static void bpy_prop_callback_assign_string(struct PropertyRNA *prop, PyObject *get_fn, - PyObject *set_fn) + PyObject *set_fn, + PyObject *search_fn, + const eStringPropertySearchFlag search_flag) { StringPropertyGetFunc rna_get_fn = NULL; StringPropertyLengthFunc rna_length_fn = NULL; StringPropertySetFunc rna_set_fn = NULL; + StringPropertySearchFunc rna_search_fn = NULL; if (get_fn && get_fn != Py_None) { struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop); @@ -2372,8 +2419,17 @@ static void bpy_prop_callback_assign_string(struct PropertyRNA *prop, rna_set_fn = bpy_prop_string_set_fn; ASSIGN_PYOBJECT_INCREF(prop_store->py_data.set_fn, set_fn); } + if (search_fn) { + struct BPyPropStore *prop_store = bpy_prop_py_data_ensure(prop); + + rna_search_fn = bpy_prop_string_visit_for_search_fn; + ASSIGN_PYOBJECT_INCREF(prop_store->py_data.string_data.search_fn, search_fn); + } RNA_def_property_string_funcs_runtime(prop, rna_get_fn, rna_length_fn, rna_set_fn); + if (rna_search_fn) { + RNA_def_property_string_search_func_runtime(prop, rna_search_fn, search_flag); + } } static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, @@ -2565,8 +2621,7 @@ static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p) " :type description: string\n" #define BPY_PROPDEF_UNIT_DOC \ - " :arg unit: Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', " \ - "'VELOCITY', 'ACCELERATION', 'MASS', 'CAMERA', 'POWER'].\n" \ + " :arg unit: Enumerator in :ref:`rna_enum_property_unit_items`.\n" \ " :type unit: string\n" #define BPY_PROPDEF_NUM_MIN_DOC \ @@ -2628,6 +2683,24 @@ static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p) " This function must take 2 values (self, value) and return None.\n" \ " :type set: function\n" +#define BPY_PROPDEF_SEARCH_DOC \ + " :arg search: Function to be called to show candidates for this string (shown in the UI).\n" \ + " This function must take 3 values (self, context, edit_text)\n" \ + " and return a sequence, iterator or generator where each item must be:\n" \ + "\n" \ + " - A single string (representing a candidate to display).\n" \ + " - A tuple-pair of strings, where the first is a candidate and the second\n" \ + " is additional information about the candidate.\n" \ + " :type search: function\n" \ + " :arg search_options: Set of strings in:\n" \ + "\n" \ + " - 'SORT' sorts the resulting items.\n" \ + " - 'SUGGESTION' lets the user enter values not found in search candidates.\n" \ + " **WARNING** disabling this flag causes the search callback to run on redraw,\n" \ + " so only disable this flag if it's not likely to cause performance issues.\n" \ + "\n" \ + " :type search_options: set\n" + #define BPY_PROPDEF_POINTER_TYPE_DOC \ " :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \ " :type type: class\n" @@ -2697,18 +2770,18 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) bool default_value = false; PropertyRNA *prop; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_number_items, + .items = rna_enum_property_subtype_number_items, .value = PROP_NONE, }; @@ -2822,7 +2895,7 @@ PyDoc_STRVAR( "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC " :arg default: sequence of booleans the length of *size*.\n" " :type default: sequence\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC - BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC + BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { @@ -2845,18 +2918,18 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject PropertyRNA *prop; PyObject *default_py = NULL; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_array_items, + .items = rna_enum_property_subtype_number_array_items, .value = PROP_NONE, }; PyObject *update_fn = NULL; @@ -3021,18 +3094,18 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_number_items, + .items = rna_enum_property_subtype_number_items, .value = PROP_NONE, }; PyObject *update_fn = NULL; @@ -3168,8 +3241,8 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc, " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC - BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC + BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3194,18 +3267,18 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject PyObject *default_py = NULL; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_array_items, + .items = rna_enum_property_subtype_number_array_items, .value = PROP_NONE, }; PyObject *update_fn = NULL; @@ -3391,18 +3464,18 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_number_items, + .items = rna_enum_property_subtype_number_items, .value = PROP_NONE, }; struct BPy_EnumProperty_Parse unit_enum = { @@ -3536,8 +3609,9 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc, " :type soft_min: float\n" BPY_PROPDEF_NUM_SOFTMAX_DOC " :type soft_max: float\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC - BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_VECSIZE_DOC - BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_SUBTYPE_NUMBER_ARRAY_DOC BPY_PROPDEF_UNIT_DOC + BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC + BPY_PROPDEF_SET_DOC); static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3563,18 +3637,18 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec PyObject *default_py = NULL; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_array_items, + .items = rna_enum_property_subtype_number_array_items, .value = PROP_NONE, }; struct BPy_EnumProperty_Parse unit_enum = { @@ -3721,7 +3795,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc, "subtype='NONE', " "update=None, " "get=None, " - "set=None)\n" + "set=None, " + "search=None, " + "search_options={'SUGGESTION'})\n" "\n" " Returns a new string property definition.\n" "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC @@ -3730,7 +3806,7 @@ PyDoc_STRVAR(BPy_StringProperty_doc, " :arg maxlen: maximum length of the string.\n" " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC BPY_PROPDEF_SEARCH_DOC); static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3750,23 +3826,28 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw PropertyRNA *prop; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { .srna = srna, }; struct BPy_EnumProperty_Parse subtype_enum = { - .items = property_subtype_string_items, + .items = rna_enum_property_subtype_string_items, .value = PROP_NONE, }; PyObject *update_fn = NULL; PyObject *get_fn = NULL; PyObject *set_fn = NULL; + PyObject *search_fn = NULL; + static struct BPy_EnumProperty_Parse search_options_enum = { + .items = rna_enum_property_string_search_flag_items, + .value = PROP_STRING_SEARCH_SUGGESTION, + }; static const char *_keywords[] = { "attr", @@ -3781,6 +3862,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "update", "get", "set", + "search", + "search_options", NULL, }; static _PyArg_Parser _parser = { @@ -3797,6 +3880,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "O" /* `update` */ "O" /* `get` */ "O" /* `set` */ + "O" /* `search` */ + "O&" /* `search_options` */ ":StringProperty", _keywords, 0, @@ -3820,7 +3905,10 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw &subtype_enum, &update_fn, &get_fn, - &set_fn)) { + &set_fn, + &search_fn, + pyrna_enum_bitfield_parse_set, + &search_options_enum)) { return NULL; } @@ -3833,6 +3921,9 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { return NULL; } + if (bpy_prop_callback_check(search_fn, "search", 3) == -1) { + return NULL; + } if (id_data.prop_free_handle != NULL) { RNA_def_property_free_identifier_deferred_finish(srna, id_data.prop_free_handle); @@ -3858,7 +3949,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw bpy_prop_assign_flag_override(prop, override_enum.value); } bpy_prop_callback_assign_update(prop, update_fn); - bpy_prop_callback_assign_string(prop, get_fn, set_fn); + bpy_prop_callback_assign_string(prop, get_fn, set_fn, search_fn, search_options_enum.value); RNA_def_property_duplicate_pointers(srna, prop); Py_RETURN_NONE; @@ -3941,11 +4032,11 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_enum_items, + .items = rna_enum_property_flag_enum_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { @@ -4164,11 +4255,11 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *type = Py_None; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_items, + .items = rna_enum_property_override_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { @@ -4301,11 +4392,11 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *type = Py_None; struct BPy_EnumProperty_Parse options_enum = { - .items = property_flag_items, + .items = rna_enum_property_flag_items, .value = 0, }; struct BPy_EnumProperty_Parse override_enum = { - .items = property_flag_override_collection_items, + .items = rna_enum_property_override_flag_collection_items, .value = 0, }; struct BPy_EnumProperty_Parse_WithSRNA tags_enum = { diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2276e5e97a8..14be9ceda94 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8886,7 +8886,7 @@ static PyObject *pyrna_register_class(PyObject *UNUSED(self), PyObject *py_class return NULL; } - /* Warning: gets parent classes srna, only for the register function. */ + /* WARNING: gets parent classes srna, only for the register function. */ srna = pyrna_struct_as_srna(py_class, true, "register_class(...):"); if (srna == NULL) { return NULL; diff --git a/source/blender/python/intern/bpy_rna_text.h b/source/blender/python/intern/bpy_rna_text.h index b3854b96886..6d8ed208bc3 100644 --- a/source/blender/python/intern/bpy_rna_text.h +++ b/source/blender/python/intern/bpy_rna_text.h @@ -15,4 +15,4 @@ extern PyMethodDef BPY_rna_region_from_string_method_def; #ifdef __cplusplus } -#endif
\ No newline at end of file +#endif diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt index 747d6c0e8f8..f355d03cadc 100644 --- a/source/blender/python/mathutils/CMakeLists.txt +++ b/source/blender/python/mathutils/CMakeLists.txt @@ -4,9 +4,9 @@ set(INC . ../../blenkernel ../../blenlib - ../../imbuf ../../bmesh ../../depsgraph + ../../imbuf ../../makesdna ../../../../intern/guardedalloc ) diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 0fcde229907..88e8d880360 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -22,8 +22,40 @@ #define COLOR_SIZE 3 -/* ----------------------------------mathutils.Color() ------------------- */ -/* makes a new color for you to play with */ +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ +static PyObject *Color_to_tuple_ex(ColorObject *self, int ndigits) +{ + PyObject *ret; + int i; + + ret = PyTuple_New(COLOR_SIZE); + + if (ndigits >= 0) { + for (i = 0; i < COLOR_SIZE; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->col[i], ndigits))); + } + } + else { + for (i = 0; i < COLOR_SIZE; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->col[i])); + } + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: `__new__` / `mathutils.Color()` + * \{ */ + static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { float col[3] = {0.0f, 0.0f, 0.0f}; @@ -54,29 +86,11 @@ static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Color_CreatePyObject(col, type); } -/* -----------------------------METHODS---------------------------- */ - -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Color_ToTupleExt(ColorObject *self, int ndigits) -{ - PyObject *ret; - int i; - - ret = PyTuple_New(COLOR_SIZE); - - if (ndigits >= 0) { - for (i = 0; i < COLOR_SIZE; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->col[i], ndigits))); - } - } - else { - for (i = 0; i < COLOR_SIZE; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->col[i])); - } - } +/** \} */ - return ret; -} +/* -------------------------------------------------------------------- */ +/** \name Color Methods: Color Space Conversion + * \{ */ PyDoc_STRVAR(Color_from_scene_linear_to_srgb_doc, ".. function:: from_scene_linear_to_srgb()\n" @@ -190,7 +204,11 @@ static PyObject *Color_from_rec709_linear_to_scene_linear(ColorObject *self) return Color_CreatePyObject(col, Py_TYPE(self)); } -/* ---------------------------- Colorspace conversion -------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Methods: Color Copy/Deep-Copy + * \{ */ PyDoc_STRVAR(Color_copy_doc, ".. function:: copy()\n" @@ -218,8 +236,11 @@ static PyObject *Color_deepcopy(ColorObject *self, PyObject *args) return Color_copy(self); } -/* ----------------------------print object (internal)-------------- */ -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: `__repr__` & `__str__` + * \{ */ static PyObject *Color_repr(ColorObject *self) { @@ -229,7 +250,7 @@ static PyObject *Color_repr(ColorObject *self) return NULL; } - tuple = Color_ToTupleExt(self, -1); + tuple = Color_to_tuple_ex(self, -1); ret = PyUnicode_FromFormat("Color(%R)", tuple); @@ -255,8 +276,12 @@ static PyObject *Color_str(ColorObject *self) } #endif -/* ------------------------tp_richcmpr */ -/* returns -1 exception, 0 false, 1 true */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Rich Compare + * \{ */ + static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -295,6 +320,12 @@ static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Color_hash(ColorObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -308,15 +339,19 @@ static Py_hash_t Color_hash(ColorObject *self) return mathutils_array_hash(self->col, COLOR_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Color_len(ColorObject *UNUSED(self)) { return COLOR_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Color_item(ColorObject *self, int i) { if (i < 0) { @@ -336,8 +371,8 @@ static PyObject *Color_item(ColorObject *self, int i) return PyFloat_FromDouble(self->col[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Color_ass_item(ColorObject *self, int i, PyObject *value) { float f; @@ -373,8 +408,8 @@ static int Color_ass_item(ColorObject *self, int i, PyObject *value) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Color_slice(ColorObject *self, int begin, int end) { PyObject *tuple; @@ -398,8 +433,8 @@ static PyObject *Color_slice(ColorObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Color_ass_slice(ColorObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -436,6 +471,7 @@ static int Color_ass_slice(ColorObject *self, int begin, int end, PyObject *seq) return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Color_subscript(ColorObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -472,6 +508,7 @@ static PyObject *Color_subscript(ColorObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Color_ass_subscript(ColorObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -504,29 +541,13 @@ static int Color_ass_subscript(ColorObject *self, PyObject *item, PyObject *valu return -1; } -/* -----------------PROTCOL DECLARATIONS-------------------------- */ -static PySequenceMethods Color_SeqMethods = { - (lenfunc)Color_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Color_item, /* sq_item */ - NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Color_ass_item, /* sq_ass_item */ - NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ -}; +/** \} */ -static PyMappingMethods Color_AsMapping = { - (lenfunc)Color_len, - (binaryfunc)Color_subscript, - (objobjargproc)Color_ass_subscript, -}; - -/* numeric */ +/* -------------------------------------------------------------------- */ +/** \name Color Type: Numeric Protocol Implementation + * \{ */ -/* addition: obj + obj */ +/** Addition: `object + object`. */ static PyObject *Color_add(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -552,7 +573,7 @@ static PyObject *Color_add(PyObject *v1, PyObject *v2) return Color_CreatePyObject(col, Py_TYPE(v1)); } -/* addition in-place: obj += obj */ +/** Addition in-place: `object += object`. */ static PyObject *Color_iadd(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -579,7 +600,7 @@ static PyObject *Color_iadd(PyObject *v1, PyObject *v2) return v1; } -/* subtraction: obj - obj */ +/** Subtraction: `object - object`. */ static PyObject *Color_sub(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -605,7 +626,7 @@ static PyObject *Color_sub(PyObject *v1, PyObject *v2) return Color_CreatePyObject(col, Py_TYPE(v1)); } -/* subtraction in-place: obj -= obj */ +/** Subtraction in-place: `object -= object`. */ static PyObject *Color_isub(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -639,6 +660,7 @@ static PyObject *color_mul_float(ColorObject *color, const float scalar) return Color_CreatePyObject(tcol, Py_TYPE(color)); } +/** Multiplication: `object * object`. */ static PyObject *Color_mul(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL, *color2 = NULL; @@ -683,6 +705,7 @@ static PyObject *Color_mul(PyObject *v1, PyObject *v2) return NULL; } +/** Division: `object / object`. */ static PyObject *Color_div(PyObject *v1, PyObject *v2) { ColorObject *color1 = NULL; @@ -716,7 +739,7 @@ static PyObject *Color_div(PyObject *v1, PyObject *v2) return NULL; } -/* multiplication in-place: obj *= obj */ +/** Multiplication in-place: `object *= object`. */ static PyObject *Color_imul(PyObject *v1, PyObject *v2) { ColorObject *color = (ColorObject *)v1; @@ -744,7 +767,7 @@ static PyObject *Color_imul(PyObject *v1, PyObject *v2) return v1; } -/* multiplication in-place: obj *= obj */ +/** Division in-place: `object *= object`. */ static PyObject *Color_idiv(PyObject *v1, PyObject *v2) { ColorObject *color = (ColorObject *)v1; @@ -777,8 +800,7 @@ static PyObject *Color_idiv(PyObject *v1, PyObject *v2) return v1; } -/* -obj - * returns the negative of this object */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Color_neg(ColorObject *self) { float tcol[COLOR_SIZE]; @@ -791,6 +813,31 @@ static PyObject *Color_neg(ColorObject *self) return Color_CreatePyObject(tcol, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Protocol Declarations + * \{ */ + +static PySequenceMethods Color_SeqMethods = { + (lenfunc)Color_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Color_item, /*sq_item*/ + NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Color_ass_item, /*sq_ass_item*/ + NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ +}; + +static PyMappingMethods Color_AsMapping = { + (lenfunc)Color_len, + (binaryfunc)Color_subscript, + (objobjargproc)Color_ass_subscript, +}; + static PyNumberMethods Color_NumMethods = { (binaryfunc)Color_add, /*nb_add*/ (binaryfunc)Color_sub, /*nb_subtract*/ @@ -811,24 +858,31 @@ static PyNumberMethods Color_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - Color_iadd, /* nb_inplace_add */ - Color_isub, /* nb_inplace_subtract */ - Color_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - Color_div, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - Color_idiv, /* nb_inplace_true_divide */ - NULL, /* nb_index */ + Color_iadd, /*nb_inplace_add*/ + Color_isub, /*nb_inplace_subtract*/ + Color_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + Color_div, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + Color_idiv, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ }; -/* color channel, vector.r/g/b */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Get/Set Item Implementation + * \{ */ + +/* Color channel (RGB): `color.r/g/b`. */ + PyDoc_STRVAR(Color_channel_r_doc, "Red color channel.\n\n:type: float"); PyDoc_STRVAR(Color_channel_g_doc, "Green color channel.\n\n:type: float"); PyDoc_STRVAR(Color_channel_b_doc, "Blue color channel.\n\n:type: float"); @@ -843,7 +897,8 @@ static int Color_channel_set(ColorObject *self, PyObject *value, void *type) return Color_ass_item(self, POINTER_AS_INT(type), value); } -/* color channel (HSV), color.h/s/v */ +/* Color channel (HSV): `color.h/s/v`. */ + PyDoc_STRVAR(Color_channel_hsv_h_doc, "HSV Hue component in [0, 1].\n\n:type: float"); PyDoc_STRVAR(Color_channel_hsv_s_doc, "HSV Saturation component in [0, 1].\n\n:type: float"); PyDoc_STRVAR(Color_channel_hsv_v_doc, "HSV Value component in [0, 1].\n\n:type: float"); @@ -891,8 +946,8 @@ static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type) return 0; } -/* color channel (HSV), color.h/s/v */ PyDoc_STRVAR(Color_hsv_doc, "HSV Values in [0, 1].\n\n:type: float triplet"); +/** Color channel HSV (get): `x = color.hsv`. */ static PyObject *Color_hsv_get(ColorObject *self, void *UNUSED(closure)) { float hsv[3]; @@ -910,6 +965,7 @@ static PyObject *Color_hsv_get(ColorObject *self, void *UNUSED(closure)) return ret; } +/** Color channel HSV (set): `color.hsv = x`. */ static int Color_hsv_set(ColorObject *self, PyObject *value, void *UNUSED(closure)) { float hsv[3]; @@ -932,9 +988,12 @@ static int Color_hsv_set(ColorObject *self, PyObject *value, void *UNUSED(closur return 0; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Color_getseters[] = { {"r", (getter)Color_channel_get, (setter)Color_channel_set, Color_channel_r_doc, (void *)0}, {"g", (getter)Color_channel_get, (setter)Color_channel_set, Color_channel_g_doc, (void *)1}, @@ -977,7 +1036,12 @@ static PyGetSetDef Color_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/* -----------------------METHOD DEFINITIONS ---------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Method Definitions + * \{ */ + static struct PyMethodDef Color_methods[] = { {"copy", (PyCFunction)Color_copy, METH_NOARGS, Color_copy_doc}, {"__copy__", (PyCFunction)Color_copy, METH_NOARGS, Color_copy_doc}, @@ -986,7 +1050,7 @@ static struct PyMethodDef Color_methods[] = { /* base-math methods */ {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, - /* Colorspace methods. */ + /* Color-space methods. */ {"from_scene_linear_to_srgb", (PyCFunction)Color_from_scene_linear_to_srgb, METH_NOARGS, @@ -1022,7 +1086,12 @@ static struct PyMethodDef Color_methods[] = { {NULL, NULL, 0, NULL}, }; -/* ------------------PY_OBECT DEFINITION-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: Python Object Definition + * \{ */ + PyDoc_STRVAR( color_doc, ".. class:: Color(rgb)\n" @@ -1087,6 +1156,12 @@ PyTypeObject color_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Type: C/API Constructors + * \{ */ + PyObject *Color_CreatePyObject(const float col[3], PyTypeObject *base_type) { ColorObject *self; @@ -1156,3 +1231,5 @@ PyObject *Color_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar cb_sub return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Color.h b/source/blender/python/mathutils/mathutils_Color.h index 11a936b7bc8..b4fd2eeaa81 100644 --- a/source/blender/python/mathutils/mathutils_Color.h +++ b/source/blender/python/mathutils/mathutils_Color.h @@ -19,7 +19,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * Blender (stored in blend_data). This is an either/or struct not both. */ -/* prototypes */ +/* Prototypes. */ + PyObject *Color_CreatePyObject(const float col[3], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; PyObject *Color_CreatePyObject_wrap(float col[3], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index 909c19e8cd2..f49868dfba7 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -19,45 +19,11 @@ #define EULER_SIZE 3 -/* ----------------------------------mathutils.Euler() ------------------- */ -/* makes a new euler for you to play with */ -static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *seq = NULL; - const char *order_str = NULL; - - float eul[EULER_SIZE] = {0.0f, 0.0f, 0.0f}; - short order = EULER_ORDER_XYZ; - - if (kwds && PyDict_Size(kwds)) { - PyErr_SetString(PyExc_TypeError, - "mathutils.Euler(): " - "takes no keyword args"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "|Os:mathutils.Euler", &seq, &order_str)) { - return NULL; - } - - switch (PyTuple_GET_SIZE(args)) { - case 0: - break; - case 2: - if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1) { - return NULL; - } - ATTR_FALLTHROUGH; - case 1: - if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1) { - return NULL; - } - break; - } - return Euler_CreatePyObject(eul, order, type); -} +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ -/* internal use, assume read callback is done */ +/** Internal use, assume read callback is done. */ static const char *euler_order_str(EulerObject *self) { static const char order[][4] = {"XYZ", "XZY", "YXZ", "YZX", "ZXY", "ZYX"}; @@ -96,8 +62,10 @@ short euler_order_from_string(const char *str, const char *error_prefix) return -1; } -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Euler_ToTupleExt(EulerObject *self, int ndigits) +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ +static PyObject *Euler_to_tuple_ex(EulerObject *self, int ndigits) { PyObject *ret; int i; @@ -118,8 +86,53 @@ static PyObject *Euler_ToTupleExt(EulerObject *self, int ndigits) return ret; } -/* -----------------------------METHODS---------------------------- - * return a quaternion representation of the euler */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: `__new__` / `mathutils.Euler()` + * \{ */ + +static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *seq = NULL; + const char *order_str = NULL; + + float eul[EULER_SIZE] = {0.0f, 0.0f, 0.0f}; + short order = EULER_ORDER_XYZ; + + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "mathutils.Euler(): " + "takes no keyword args"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|Os:mathutils.Euler", &seq, &order_str)) { + return NULL; + } + + switch (PyTuple_GET_SIZE(args)) { + case 0: + break; + case 2: + if ((order = euler_order_from_string(order_str, "mathutils.Euler()")) == -1) { + return NULL; + } + ATTR_FALLTHROUGH; + case 1: + if (mathutils_array_parse(eul, EULER_SIZE, EULER_SIZE, seq, "mathutils.Euler()") == -1) { + return NULL; + } + break; + } + return Euler_CreatePyObject(eul, order, type); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Methods + * \{ */ PyDoc_STRVAR(Euler_to_quaternion_doc, ".. method:: to_quaternion()\n" @@ -141,7 +154,6 @@ static PyObject *Euler_to_quaternion(EulerObject *self) return Quaternion_CreatePyObject(quat, NULL); } -/* return a matrix representation of the euler */ PyDoc_STRVAR(Euler_to_matrix_doc, ".. method:: to_matrix()\n" "\n" @@ -279,9 +291,6 @@ static PyObject *Euler_make_compatible(EulerObject *self, PyObject *value) Py_RETURN_NONE; } -/* ----------------------------Euler.rotate()----------------------- - * return a copy of the euler */ - PyDoc_STRVAR(Euler_copy_doc, ".. function:: copy()\n" "\n" @@ -308,8 +317,11 @@ static PyObject *Euler_deepcopy(EulerObject *self, PyObject *args) return Euler_copy(self); } -/* ----------------------------print object (internal)-------------- - * print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: `__repr__` & `__str__` + * \{ */ static PyObject *Euler_repr(EulerObject *self) { @@ -319,7 +331,7 @@ static PyObject *Euler_repr(EulerObject *self) return NULL; } - tuple = Euler_ToTupleExt(self, -1); + tuple = Euler_to_tuple_ex(self, -1); ret = PyUnicode_FromFormat("Euler(%R, '%s')", tuple, euler_order_str(self)); @@ -349,6 +361,12 @@ static PyObject *Euler_str(EulerObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Rich Compare + * \{ */ + static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -390,6 +408,12 @@ static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Euler_hash(EulerObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -403,15 +427,19 @@ static Py_hash_t Euler_hash(EulerObject *self) return mathutils_array_hash(self->eul, EULER_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Sequence Protocol + * \{ */ + +/** Sequence length: `len(object)`. */ static int Euler_len(EulerObject *UNUSED(self)) { return EULER_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Euler_item(EulerObject *self, int i) { if (i < 0) { @@ -431,8 +459,8 @@ static PyObject *Euler_item(EulerObject *self, int i) return PyFloat_FromDouble(self->eul[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Euler_ass_item(EulerObject *self, int i, PyObject *value) { float f; @@ -468,8 +496,8 @@ static int Euler_ass_item(EulerObject *self, int i, PyObject *value) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Euler_slice(EulerObject *self, int begin, int end) { PyObject *tuple; @@ -493,8 +521,8 @@ static PyObject *Euler_slice(EulerObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -531,6 +559,7 @@ static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq) return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Euler_subscript(EulerObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -567,6 +596,7 @@ static PyObject *Euler_subscript(EulerObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -599,18 +629,23 @@ static int Euler_ass_subscript(EulerObject *self, PyObject *item, PyObject *valu return -1; } -/* -----------------PROTCOL DECLARATIONS-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Sequence & Mapping Protocol Declarations + * \{ */ + static PySequenceMethods Euler_SeqMethods = { - (lenfunc)Euler_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Euler_item, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice (deprecated) */ - (ssizeobjargproc)Euler_ass_item, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice (deprecated) */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Euler_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Euler_item, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Euler_ass_item, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ }; static PyMappingMethods Euler_AsMapping = { @@ -619,7 +654,13 @@ static PyMappingMethods Euler_AsMapping = { (objobjargproc)Euler_ass_subscript, }; -/* euler axis, euler.x/y/z */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Get/Set Item Implementation + * \{ */ + +/* Euler axis: `euler.x/y/z`. */ PyDoc_STRVAR(Euler_axis_doc, "Euler axis angle in radians.\n\n:type: float"); static PyObject *Euler_axis_get(EulerObject *self, void *type) @@ -632,7 +673,7 @@ static int Euler_axis_set(EulerObject *self, PyObject *value, void *type) return Euler_ass_item(self, POINTER_AS_INT(type), value); } -/* rotation order */ +/* Euler rotation order: `euler.order`. */ PyDoc_STRVAR( Euler_order_doc, @@ -666,9 +707,12 @@ static int Euler_order_set(EulerObject *self, PyObject *value, void *UNUSED(clos return 0; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Euler_getseters[] = { {"x", (getter)Euler_axis_get, (setter)Euler_axis_set, Euler_axis_doc, (void *)0}, {"y", (getter)Euler_axis_get, (setter)Euler_axis_set, Euler_axis_doc, (void *)1}, @@ -694,7 +738,12 @@ static PyGetSetDef Euler_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/* -----------------------METHOD DEFINITIONS ---------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Method Definitions + * \{ */ + static struct PyMethodDef Euler_methods[] = { {"zero", (PyCFunction)Euler_zero, METH_NOARGS, Euler_zero_doc}, {"to_matrix", (PyCFunction)Euler_to_matrix, METH_NOARGS, Euler_to_matrix_doc}, @@ -711,7 +760,12 @@ static struct PyMethodDef Euler_methods[] = { {NULL, NULL, 0, NULL}, }; -/* ------------------PY_OBECT DEFINITION-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: Python Object Definition + * \{ */ + PyDoc_STRVAR( euler_doc, ".. class:: Euler(angles, order='XYZ')\n" @@ -776,6 +830,12 @@ PyTypeObject euler_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Euler Type: C/API Constructors + * \{ */ + PyObject *Euler_CreatePyObject(const float eul[3], const short order, PyTypeObject *base_type) { EulerObject *self; @@ -849,3 +909,5 @@ PyObject *Euler_CreatePyObject_cb(PyObject *cb_user, return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Euler.h b/source/blender/python/mathutils/mathutils_Euler.h index 4438ee380af..ca2ca26c90a 100644 --- a/source/blender/python/mathutils/mathutils_Euler.h +++ b/source/blender/python/mathutils/mathutils_Euler.h @@ -22,6 +22,7 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both */ /* prototypes */ + PyObject *Euler_CreatePyObject(const float eul[3], short order, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 76b5424711f..8cd7a5c7d87 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -32,6 +32,10 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), MatrixObject *self); static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrixAccess_t type); +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + static int matrix_row_vector_check(MatrixObject *mat, VectorObject *vec, int row) { if ((vec->vec_num != mat->col_num) || (row >= mat->row_num)) { @@ -56,9 +60,242 @@ static int matrix_col_vector_check(MatrixObject *mat, VectorObject *vec, int col return 1; } -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix[i][j] = val OR matrix.row[i][j] = val */ +/** When a matrix is 4x4 size but initialized as a 3x3, re-assign values for 4x4. */ +static void matrix_3x3_as_4x4(float mat[16]) +{ + mat[10] = mat[8]; + mat[9] = mat[7]; + mat[8] = mat[6]; + mat[7] = 0.0f; + mat[6] = mat[5]; + mat[5] = mat[4]; + mat[4] = mat[3]; + mat[3] = 0.0f; +} + +void matrix_as_3x3(float mat[3][3], MatrixObject *self) +{ + copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0)); + copy_v3_v3(mat[1], MATRIX_COL_PTR(self, 1)); + copy_v3_v3(mat[2], MATRIX_COL_PTR(self, 2)); +} + +static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src) +{ + BLI_assert((mat_dst->col_num == mat_src->col_num) && (mat_dst->row_num == mat_src->row_num)); + BLI_assert(mat_dst != mat_src); + + memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->col_num * mat_dst->row_num)); +} + +static void matrix_unit_internal(MatrixObject *self) +{ + const int mat_size = sizeof(float) * (self->col_num * self->row_num); + memset(self->matrix, 0x0, mat_size); + const int col_row_max = min_ii(self->col_num, self->row_num); + const int row_num = self->row_num; + for (int col = 0; col < col_row_max; col++) { + self->matrix[(col * row_num) + col] = 1.0f; + } +} + +/** Transposes memory layout, row/columns don't have to match. */ +static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src) +{ + ushort col, row; + uint i = 0; + + for (row = 0; row < mat_src->row_num; row++) { + for (col = 0; col < mat_src->col_num; col++) { + mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col); + } + } +} + +/** Assumes `rowsize == colsize` is checked and the read callback has run. */ +static float matrix_determinant_internal(const MatrixObject *self) +{ + if (self->col_num == 2) { + return determinant_m2(MATRIX_ITEM(self, 0, 0), + MATRIX_ITEM(self, 0, 1), + MATRIX_ITEM(self, 1, 0), + MATRIX_ITEM(self, 1, 1)); + } + if (self->col_num == 3) { + return determinant_m3(MATRIX_ITEM(self, 0, 0), + MATRIX_ITEM(self, 0, 1), + MATRIX_ITEM(self, 0, 2), + MATRIX_ITEM(self, 1, 0), + MATRIX_ITEM(self, 1, 1), + MATRIX_ITEM(self, 1, 2), + MATRIX_ITEM(self, 2, 0), + MATRIX_ITEM(self, 2, 1), + MATRIX_ITEM(self, 2, 2)); + } + + return determinant_m4((const float(*)[4])self->matrix); +} + +static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim) +{ + /* calculate the classical adjoint */ + switch (dim) { + case 2: { + adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src); + break; + } + case 3: { + adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src); + break; + } + case 4: { + adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src); + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +static void matrix_invert_with_det_n_internal(float *mat_dst, + const float *mat_src, + const float det, + const ushort dim) +{ + float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; + ushort i, j, k; + + BLI_assert(det != 0.0f); + + adjoint_matrix_n(mat, mat_src, dim); + + /* divide by determinant & set values */ + k = 0; + for (i = 0; i < dim; i++) { /* col_num */ + for (j = 0; j < dim; j++) { /* row_num */ + mat_dst[MATRIX_ITEM_INDEX_NUMROW(dim, j, i)] = mat[k++] / det; + } + } +} + +/** + * \param r_mat: can be from `self->matrix` or not. + */ +static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) +{ + float det; + BLI_assert(self->col_num == self->row_num); + det = matrix_determinant_internal(self); + + if (det != 0.0f) { + matrix_invert_with_det_n_internal(r_mat, self->matrix, det, self->col_num); + return true; + } + + return false; +} + +/** + * Similar to `matrix_invert_internal` but should never error. + * \param r_mat: can be from `self->matrix` or not. + */ +static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) +{ + float det; + float *in_mat = self->matrix; + BLI_assert(self->col_num == self->row_num); + det = matrix_determinant_internal(self); + + if (det == 0.0f) { + const float eps = PSEUDOINVERSE_EPSILON; + + /* We will copy self->matrix into r_mat (if needed), + * and modify it in place to add diagonal epsilon. */ + in_mat = r_mat; + + switch (self->col_num) { + case 2: { + float(*mat)[2] = (float(*)[2])in_mat; + + if (in_mat != self->matrix) { + copy_m2_m2(mat, (const float(*)[2])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + + if (UNLIKELY((det = determinant_m2(mat[0][0], mat[0][1], mat[1][0], mat[1][1])) == 0.0f)) { + unit_m2(mat); + det = 1.0f; + } + break; + } + case 3: { + float(*mat)[3] = (float(*)[3])in_mat; + + if (in_mat != self->matrix) { + copy_m3_m3(mat, (const float(*)[3])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + mat[2][2] += eps; + + if (UNLIKELY((det = determinant_m3_array(mat)) == 0.0f)) { + unit_m3(mat); + det = 1.0f; + } + break; + } + case 4: { + float(*mat)[4] = (float(*)[4])in_mat; + + if (in_mat != self->matrix) { + copy_m4_m4(mat, (const float(*)[4])self->matrix); + } + mat[0][0] += eps; + mat[1][1] += eps; + mat[2][2] += eps; + mat[3][3] += eps; + + if (UNLIKELY(det = determinant_m4(mat)) == 0.0f) { + unit_m4(mat); + det = 1.0f; + } + break; + } + default: + BLI_assert_unreachable(); + } + } + + matrix_invert_with_det_n_internal(r_mat, in_mat, det, self->col_num); +} + +static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), + MatrixObject *self) +{ + PyObject *ret = Matrix_copy(self); + if (ret) { + PyObject *ret_dummy = matrix_func((MatrixObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return ret; + } + /* error */ + Py_DECREF(ret); + return NULL; + } + + /* copy may fail if the read callback errors out */ + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Row Callbacks + * This is so you can do `matrix[i][j] = val` or `matrix.row[i][j] = val`. + * \{ */ uchar mathutils_matrix_row_cb_index = -1; @@ -147,9 +384,12 @@ Mathutils_Callback mathutils_matrix_row_cb = { mathutils_matrix_row_set_index, }; -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix.col[i][j] = val */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Column Callbacks + * This is so you can do `matrix.col[i][j] = val`. + * \{ */ uchar mathutils_matrix_col_cb_index = -1; @@ -246,10 +486,14 @@ Mathutils_Callback mathutils_matrix_col_cb = { mathutils_matrix_col_set_index, }; -/* ---------------------------------------------------------------------------- - * matrix row callbacks - * this is so you can do matrix.translation = val - * NOTE: this is _exactly like matrix.col except the 4th component is always omitted. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Translation Callbacks + * This is so you can do `matrix.translation = val`. + * + * \note this is _exactly like matrix.col except the 4th component is always omitted. + * \{ */ uchar mathutils_matrix_translation_cb_index = -1; @@ -326,11 +570,12 @@ Mathutils_Callback mathutils_matrix_translation_cb = { mathutils_matrix_translation_set_index, }; -/* matrix column callbacks, this is so you can do `matrix.translation = Vector()`. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: `__new__` / `mathutils.Matrix()` + * \{ */ -/* ----------------------------------mathutils.Matrix() ----------------- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ -/* create a new matrix type */ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds)) { @@ -379,41 +624,13 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } -static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), - MatrixObject *self) -{ - PyObject *ret = Matrix_copy(self); - if (ret) { - PyObject *ret_dummy = matrix_func((MatrixObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return ret; - } - /* error */ - Py_DECREF(ret); - return NULL; - } - - /* copy may fail if the read callback errors out */ - return NULL; -} - -/* when a matrix is 4x4 size but initialized as a 3x3, re-assign values for 4x4 */ -static void matrix_3x3_as_4x4(float mat[16]) -{ - mat[10] = mat[8]; - mat[9] = mat[7]; - mat[8] = mat[6]; - mat[7] = 0.0f; - mat[6] = mat[5]; - mat[5] = mat[4]; - mat[4] = mat[3]; - mat[3] = 0.0f; -} +/** \} */ -/*-----------------------CLASS-METHODS----------------------------*/ +/* -------------------------------------------------------------------- */ +/** \name Matrix Class Methods + * \{ */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ +/** Identity constructor: `mathutils.Matrix.Identity()`. */ PyDoc_STRVAR(C_Matrix_Identity_doc, ".. classmethod:: Identity(size)\n" "\n" @@ -441,6 +658,7 @@ static PyObject *C_Matrix_Identity(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(NULL, matSize, matSize, (PyTypeObject *)cls); } +/** Rotation constructor: `mathutils.Matrix.Rotation()`. */ PyDoc_STRVAR(C_Matrix_Rotation_doc, ".. classmethod:: Rotation(angle, size, axis)\n" "\n" @@ -460,25 +678,8 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) PyObject *vec = NULL; const char *axis = NULL; int matSize; - double angle; /* use double because of precision problems at high values */ - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + double angle; /* Use double because of precision problems at high values. */ + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "di|O:Matrix.Rotation", &angle, &matSize, &vec)) { return NULL; @@ -545,6 +746,7 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } +/** Translation constructor: `mathutils.Matrix.Translation()`. */ PyDoc_STRVAR(C_Matrix_Translation_doc, ".. classmethod:: Translation(vector)\n" "\n" @@ -567,7 +769,7 @@ static PyObject *C_Matrix_Translation(PyObject *cls, PyObject *value) return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.Diagonal() ------------- */ + PyDoc_STRVAR(C_Matrix_Diagonal_doc, ".. classmethod:: Diagonal(vector)\n" "\n" @@ -577,6 +779,7 @@ PyDoc_STRVAR(C_Matrix_Diagonal_doc, " :type vector: :class:`Vector`\n" " :return: A diagonal matrix.\n" " :rtype: :class:`Matrix`\n"); +/** Diagonal constructor: `mathutils.Matrix.Diagonal()`. */ static PyObject *C_Matrix_Diagonal(PyObject *cls, PyObject *value) { float mat[16] = {0.0f}; @@ -595,8 +798,8 @@ static PyObject *C_Matrix_Diagonal(PyObject *cls, PyObject *value) return Matrix_CreatePyObject(mat, size, size, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.Scale() ------------- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ + +/** Scale constructor: `mathutils.Matrix.Scale()`. */ PyDoc_STRVAR(C_Matrix_Scale_doc, ".. classmethod:: Scale(factor, size, axis)\n" "\n" @@ -617,24 +820,7 @@ static PyObject *C_Matrix_Scale(PyObject *cls, PyObject *args) float tvec[3]; float factor; int matSize; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "fi|O:Matrix.Scale", &factor, &matSize, &vec)) { return NULL; @@ -700,8 +886,7 @@ static PyObject *C_Matrix_Scale(PyObject *cls, PyObject *args) /* pass to matrix creation */ return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } -/* ----------------------------------mathutils.Matrix.OrthoProjection() --- */ -/* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ +/** Orthographic projection constructor: `mathutils.Matrix.OrthoProjection()`. */ PyDoc_STRVAR(C_Matrix_OrthoProjection_doc, ".. classmethod:: OrthoProjection(axis, size)\n" "\n" @@ -721,24 +906,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) int matSize, x; float norm = 0.0f; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "Oi:Matrix.OrthoProjection", &axis, &matSize)) { return NULL; @@ -837,6 +1005,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } +/** Shear constructor: `mathutils.Matrix.Shear()`. */ PyDoc_STRVAR(C_Matrix_Shear_doc, ".. classmethod:: Shear(plane, size, factor)\n" "\n" @@ -857,24 +1026,7 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args) int matSize; const char *plane; PyObject *fac; - float mat[16] = { - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - }; + float mat[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; if (!PyArg_ParseTuple(args, "siO:Matrix.Shear", &plane, &matSize, &fac)) { return NULL; @@ -1051,205 +1203,12 @@ static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args) return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); } -void matrix_as_3x3(float mat[3][3], MatrixObject *self) -{ - copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0)); - copy_v3_v3(mat[1], MATRIX_COL_PTR(self, 1)); - copy_v3_v3(mat[2], MATRIX_COL_PTR(self, 2)); -} - -static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src) -{ - BLI_assert((mat_dst->col_num == mat_src->col_num) && (mat_dst->row_num == mat_src->row_num)); - BLI_assert(mat_dst != mat_src); +/** \} */ - memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->col_num * mat_dst->row_num)); -} +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Quaternion + * \{ */ -static void matrix_unit_internal(MatrixObject *self) -{ - const int mat_size = sizeof(float) * (self->col_num * self->row_num); - memset(self->matrix, 0x0, mat_size); - const int col_row_max = min_ii(self->col_num, self->row_num); - const int row_num = self->row_num; - for (int col = 0; col < col_row_max; col++) { - self->matrix[(col * row_num) + col] = 1.0f; - } -} - -/* transposes memory layout, rol/col's don't have to match */ -static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src) -{ - ushort col, row; - uint i = 0; - - for (row = 0; row < mat_src->row_num; row++) { - for (col = 0; col < mat_src->col_num; col++) { - mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col); - } - } -} - -/* assumes rowsize == colsize is checked and the read callback has run */ -static float matrix_determinant_internal(const MatrixObject *self) -{ - if (self->col_num == 2) { - return determinant_m2(MATRIX_ITEM(self, 0, 0), - MATRIX_ITEM(self, 0, 1), - MATRIX_ITEM(self, 1, 0), - MATRIX_ITEM(self, 1, 1)); - } - if (self->col_num == 3) { - return determinant_m3(MATRIX_ITEM(self, 0, 0), - MATRIX_ITEM(self, 0, 1), - MATRIX_ITEM(self, 0, 2), - MATRIX_ITEM(self, 1, 0), - MATRIX_ITEM(self, 1, 1), - MATRIX_ITEM(self, 1, 2), - MATRIX_ITEM(self, 2, 0), - MATRIX_ITEM(self, 2, 1), - MATRIX_ITEM(self, 2, 2)); - } - - return determinant_m4((const float(*)[4])self->matrix); -} - -static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim) -{ - /* calculate the classical adjoint */ - switch (dim) { - case 2: { - adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src); - break; - } - case 3: { - adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src); - break; - } - case 4: { - adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src); - break; - } - default: - BLI_assert_unreachable(); - break; - } -} - -static void matrix_invert_with_det_n_internal(float *mat_dst, - const float *mat_src, - const float det, - const ushort dim) -{ - float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; - ushort i, j, k; - - BLI_assert(det != 0.0f); - - adjoint_matrix_n(mat, mat_src, dim); - - /* divide by determinant & set values */ - k = 0; - for (i = 0; i < dim; i++) { /* col_num */ - for (j = 0; j < dim; j++) { /* row_num */ - mat_dst[MATRIX_ITEM_INDEX_NUMROW(dim, j, i)] = mat[k++] / det; - } - } -} - -/** - * \param r_mat: can be from `self->matrix` or not. - */ -static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) -{ - float det; - BLI_assert(self->col_num == self->row_num); - det = matrix_determinant_internal(self); - - if (det != 0.0f) { - matrix_invert_with_det_n_internal(r_mat, self->matrix, det, self->col_num); - return true; - } - - return false; -} - -/** - * Similar to `matrix_invert_internal` but should never error. - * \param r_mat: can be from `self->matrix` or not. - */ -static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) -{ - float det; - float *in_mat = self->matrix; - BLI_assert(self->col_num == self->row_num); - det = matrix_determinant_internal(self); - - if (det == 0.0f) { - const float eps = PSEUDOINVERSE_EPSILON; - - /* We will copy self->matrix into r_mat (if needed), - * and modify it in place to add diagonal epsilon. */ - in_mat = r_mat; - - switch (self->col_num) { - case 2: { - float(*mat)[2] = (float(*)[2])in_mat; - - if (in_mat != self->matrix) { - copy_m2_m2(mat, (const float(*)[2])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - - if (UNLIKELY((det = determinant_m2(mat[0][0], mat[0][1], mat[1][0], mat[1][1])) == 0.0f)) { - unit_m2(mat); - det = 1.0f; - } - break; - } - case 3: { - float(*mat)[3] = (float(*)[3])in_mat; - - if (in_mat != self->matrix) { - copy_m3_m3(mat, (const float(*)[3])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - mat[2][2] += eps; - - if (UNLIKELY((det = determinant_m3_array(mat)) == 0.0f)) { - unit_m3(mat); - det = 1.0f; - } - break; - } - case 4: { - float(*mat)[4] = (float(*)[4])in_mat; - - if (in_mat != self->matrix) { - copy_m4_m4(mat, (const float(*)[4])self->matrix); - } - mat[0][0] += eps; - mat[1][1] += eps; - mat[2][2] += eps; - mat[3][3] += eps; - - if (UNLIKELY(det = determinant_m4(mat)) == 0.0f) { - unit_m4(mat); - det = 1.0f; - } - break; - } - default: - BLI_assert_unreachable(); - } - } - - matrix_invert_with_det_n_internal(r_mat, in_mat, det, self->col_num); -} - -/*-----------------------------METHODS----------------------------*/ PyDoc_STRVAR(Matrix_to_quaternion_doc, ".. method:: to_quaternion()\n" "\n" @@ -1282,7 +1241,12 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self) return Quaternion_CreatePyObject(quat, NULL); } -/*---------------------------matrix.toEuler() --------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Euler + * \{ */ + PyDoc_STRVAR(Matrix_to_euler_doc, ".. method:: to_euler(order, euler_compat)\n" "\n" @@ -1367,6 +1331,12 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args) return Euler_CreatePyObject(eul, order, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Resize + * \{ */ + PyDoc_STRVAR(Matrix_resize_4x4_doc, ".. method:: resize_4x4()\n" "\n" @@ -1411,6 +1381,12 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To NxN + * \{ */ + static PyObject *Matrix_to_NxN(MatrixObject *self, const int col_num, const int row_num) { const int mat_size = sizeof(float) * (col_num * row_num); @@ -1480,6 +1456,12 @@ static PyObject *Matrix_to_4x4(MatrixObject *self) return Matrix_to_NxN(self, 4, 4); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: To Translation/Scale + * \{ */ + PyDoc_STRVAR(Matrix_to_translation_doc, ".. method:: to_translation()\n" "\n" @@ -1539,9 +1521,13 @@ static PyObject *Matrix_to_scale(MatrixObject *self) return Vector_CreatePyObject(size, 3, NULL); } -/*---------------------------matrix.invert() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Invert + * \{ */ -/* re-usable checks for invert */ +/** Re-usable checks for invert. */ static bool matrix_invert_is_compat(const MatrixObject *self) { if (self->col_num != self->row_num) { @@ -1763,7 +1749,12 @@ static PyObject *Matrix_inverted_safe(MatrixObject *self) return Matrix_copy_notest(self, mat); } -/*---------------------------matrix.adjugate() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Adjugate + * \{ */ + PyDoc_STRVAR( Matrix_adjugate_doc, ".. method:: adjugate()\n" @@ -1852,7 +1843,12 @@ static PyObject *Matrix_rotate(MatrixObject *self, PyObject *value) Py_RETURN_NONE; } -/*---------------------------matrix.decompose() ---------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Decompose + * \{ */ + PyDoc_STRVAR(Matrix_decompose_doc, ".. method:: decompose()\n" "\n" @@ -1890,6 +1886,12 @@ static PyObject *Matrix_decompose(MatrixObject *self) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Linear Interpolate (lerp) + * \{ */ + PyDoc_STRVAR(Matrix_lerp_doc, ".. function:: lerp(other, factor)\n" "\n" @@ -1947,7 +1949,6 @@ static PyObject *Matrix_lerp(MatrixObject *self, PyObject *args) return Matrix_CreatePyObject(mat, self->col_num, self->row_num, Py_TYPE(self)); } -/*---------------------------matrix.determinant() ----------------*/ PyDoc_STRVAR( Matrix_determinant_doc, ".. method:: determinant()\n" @@ -1973,7 +1974,13 @@ static PyObject *Matrix_determinant(MatrixObject *self) return PyFloat_FromDouble((double)matrix_determinant_internal(self)); } -/*---------------------------matrix.transpose() ------------------*/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Transpose + * \{ */ + PyDoc_STRVAR( Matrix_transpose_doc, ".. method:: transpose()\n" @@ -2022,7 +2029,12 @@ static PyObject *Matrix_transposed(MatrixObject *self) return matrix__apply_to_copy(Matrix_transpose, self); } -/*---------------------------matrix.normalize() ------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Normalize + * \{ */ + PyDoc_STRVAR(Matrix_normalize_doc, ".. method:: normalize()\n" "\n" @@ -2068,7 +2080,12 @@ static PyObject *Matrix_normalized(MatrixObject *self) return matrix__apply_to_copy(Matrix_normalize, self); } -/*---------------------------matrix.zero() -----------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Zero + * \{ */ + PyDoc_STRVAR(Matrix_zero_doc, ".. method:: zero()\n" "\n" @@ -2089,7 +2106,13 @@ static PyObject *Matrix_zero(MatrixObject *self) Py_RETURN_NONE; } -/*---------------------------matrix.identity(() ------------------*/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Set Identity + * \{ */ + static void matrix_identity_internal(MatrixObject *self) { BLI_assert((self->col_num == self->row_num) && (self->row_num <= 4)); @@ -2137,8 +2160,13 @@ static PyObject *Matrix_identity(MatrixObject *self) Py_RETURN_NONE; } -/*---------------------------Matrix.copy() ------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Methods: Copy + * \{ */ +/** Copy `Matrix.copy()` */ static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix) { return Matrix_CreatePyObject((const float *)matrix, self->col_num, self->row_num, Py_TYPE(self)); @@ -2159,6 +2187,8 @@ static PyObject *Matrix_copy(MatrixObject *self) return Matrix_copy_notest(self, self->matrix); } + +/** Deep-copy `Matrix.deepcopy()` */ static PyObject *Matrix_deepcopy(MatrixObject *self, PyObject *args) { if (!PyC_CheckArgs_DeepCopy(args)) { @@ -2167,8 +2197,12 @@ static PyObject *Matrix_deepcopy(MatrixObject *self, PyObject *args) return Matrix_copy(self); } -/*----------------------------print object (internal)-------------*/ -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Matrix_repr(MatrixObject *self) { int col, row; @@ -2257,6 +2291,12 @@ static PyObject *Matrix_str(MatrixObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Rich Compare + * \{ */ + static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -2298,6 +2338,12 @@ static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Matrix_hash(MatrixObject *self) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2315,16 +2361,22 @@ static Py_hash_t Matrix_hash(MatrixObject *self) return mathutils_array_hash(mat, self->row_num * self->col_num); } -/*---------------------SEQUENCE PROTOCOLS------------------------ - * ----------------------------len(object)------------------------ - * sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Sequence & Mapping Protocol Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Matrix_len(MatrixObject *self) { return self->row_num; } -/*----------------------------object[]--------------------------- - * sequence accessor (get) - * the wrapped vector gives direct access to the matrix data */ + +/** + * Sequence accessor (get): `x = object[i]`. + * \note the wrapped vector gives direct access to the matrix data. + */ static PyObject *Matrix_item_row(MatrixObject *self, int row) { if (BaseMath_ReadCallback_ForWrite(self) == -1) { @@ -2340,7 +2392,10 @@ static PyObject *Matrix_item_row(MatrixObject *self, int row) return Vector_CreatePyObject_cb( (PyObject *)self, self->col_num, mathutils_matrix_row_cb_index, row); } -/* same but column access */ +/** + * Sequence accessor (get): `x = object.col[i]`. + * \note the wrapped vector gives direct access to the matrix data. + */ static PyObject *Matrix_item_col(MatrixObject *self, int col) { if (BaseMath_ReadCallback_ForWrite(self) == -1) { @@ -2357,9 +2412,7 @@ static PyObject *Matrix_item_col(MatrixObject *self, int col) (PyObject *)self, self->row_num, mathutils_matrix_col_cb_index, col); } -/*----------------------------object[]------------------------- - * sequence accessor (set) */ - +/** Sequence accessor (set): `object[i] = x`. */ static int Matrix_ass_item_row(MatrixObject *self, int row, PyObject *value) { int col; @@ -2386,6 +2439,8 @@ static int Matrix_ass_item_row(MatrixObject *self, int row, PyObject *value) (void)BaseMath_WriteCallback(self); return 0; } + +/** Sequence accessor (set): `object.col[i] = x`. */ static int Matrix_ass_item_col(MatrixObject *self, int col, PyObject *value) { int row; @@ -2413,8 +2468,7 @@ static int Matrix_ass_item_col(MatrixObject *self, int col, PyObject *value) return 0; } -/*----------------------------object[z:y]------------------------ - * Sequence slice (get). */ +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Matrix_slice(MatrixObject *self, int begin, int end) { @@ -2439,8 +2493,8 @@ static PyObject *Matrix_slice(MatrixObject *self, int begin, int end) return tuple; } -/*----------------------------object[z:y]------------------------ - * Sequence slice (set). */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *value) { PyObject *value_fast; @@ -2500,8 +2554,84 @@ static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *va (void)BaseMath_WriteCallback(self); return 0; } -/*------------------------NUMERIC PROTOCOLS---------------------- - *------------------------obj + obj------------------------------*/ + +/** Sequence generic subscript (get): `x = object[...]`. */ +static PyObject *Matrix_subscript(MatrixObject *self, PyObject *item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return NULL; + } + if (i < 0) { + i += self->row_num; + } + return Matrix_item_row(self, i); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyTuple_New(0); + } + if (step == 1) { + return Matrix_slice(self, start, stop); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); + return NULL; + } + + PyErr_Format( + PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return NULL; +} + +/** Sequence generic subscript (set): `object[...] = x`. */ +static int Matrix_ass_subscript(MatrixObject *self, PyObject *item, PyObject *value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + if (i < 0) { + i += self->row_num; + } + return Matrix_ass_item_row(self, i, value); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + if (step == 1) { + return Matrix_ass_slice(self, start, stop, value); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); + return -1; + } + + PyErr_Format( + PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Matrix_add(PyObject *m1, PyObject *m2) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2534,8 +2664,8 @@ static PyObject *Matrix_add(PyObject *m1, PyObject *m2) return Matrix_CreatePyObject(mat, mat1->col_num, mat1->row_num, Py_TYPE(mat1)); } -/*------------------------obj - obj------------------------------ - * subtraction */ + +/** Subtraction: `object - object`. */ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2) { float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2568,8 +2698,8 @@ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2) return Matrix_CreatePyObject(mat, mat1->col_num, mat1->row_num, Py_TYPE(mat1)); } -/*------------------------obj * obj------------------------------ - * element-wise multiplication */ + +/** Multiplication (element-wise): `object * object`. */ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar) { float tmat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2631,8 +2761,8 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) Py_TYPE(m2)->tp_name); return NULL; } -/*------------------------obj *= obj------------------------------ - * In place element-wise multiplication */ + +/** Multiplication in-place (element-wise): `object *= object`. */ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) { float scalar; @@ -2680,8 +2810,8 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) Py_INCREF(m1); return m1; } -/*------------------------obj @ obj------------------------------ - * matrix multiplication */ + +/** Multiplication (matrix multiply): `object @ object`. */ static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2) { int vec_num; @@ -2756,8 +2886,8 @@ static PyObject *Matrix_matmul(PyObject *m1, PyObject *m2) Py_TYPE(m2)->tp_name); return NULL; } -/*------------------------obj @= obj------------------------------ - * In place matrix multiplication */ + +/** Multiplication in-place (matrix multiply): `object @= object`. */ static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2) { MatrixObject *mat1 = NULL, *mat2 = NULL; @@ -2816,87 +2946,24 @@ static PyObject *Matrix_imatmul(PyObject *m1, PyObject *m2) return m1; } -/*-----------------PROTOCOL DECLARATIONS--------------------------*/ -static PySequenceMethods Matrix_SeqMethods = { - (lenfunc)Matrix_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Matrix_item_row, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Matrix_ass_item_row, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ -}; - -static PyObject *Matrix_subscript(MatrixObject *self, PyObject *item) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i; - i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return NULL; - } - if (i < 0) { - i += self->row_num; - } - return Matrix_item_row(self, i); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { - return NULL; - } - - if (slicelength <= 0) { - return PyTuple_New(0); - } - if (step == 1) { - return Matrix_slice(self, start, stop); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); - return NULL; - } - - PyErr_Format( - PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return NULL; -} - -static int Matrix_ass_subscript(MatrixObject *self, PyObject *item, PyObject *value) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return -1; - } - if (i < 0) { - i += self->row_num; - } - return Matrix_ass_item_row(self, i, value); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; +/** \} */ - if (PySlice_GetIndicesEx(item, self->row_num, &start, &stop, &step, &slicelength) < 0) { - return -1; - } +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Protocol Declarations + * \{ */ - if (step == 1) { - return Matrix_ass_slice(self, start, stop, value); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with matrices"); - return -1; - } - - PyErr_Format( - PyExc_TypeError, "matrix indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return -1; -} +static PySequenceMethods Matrix_SeqMethods = { + (lenfunc)Matrix_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Matrix_item_row, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Matrix_ass_item_row, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ +}; static PyMappingMethods Matrix_AsMapping = { (lenfunc)Matrix_len, @@ -2924,25 +2991,31 @@ static PyNumberMethods Matrix_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - NULL, /* nb_inplace_add */ - NULL, /* nb_inplace_subtract */ - (binaryfunc)Matrix_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - NULL, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - NULL, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Matrix_matmul, /* nb_matrix_multiply */ - (binaryfunc)Matrix_imatmul, /* nb_inplace_matrix_multiply */ + NULL, /*nb_inplace_add*/ + NULL, /*nb_inplace_subtract*/ + (binaryfunc)Matrix_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + NULL, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + NULL, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Matrix_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Matrix_imatmul, /*nb_inplace_matrix_multiply*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Get/Set Item Implementation + * \{ */ + PyDoc_STRVAR(Matrix_translation_doc, "The translation component of the matrix.\n\n:type: Vector"); static PyObject *Matrix_translation_get(MatrixObject *self, void *UNUSED(closure)) { @@ -3099,9 +3172,12 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void return NULL; } -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Matrix_getseters[] = { {"median_scale", (getter)Matrix_median_scale_get, (setter)NULL, Matrix_median_scale_doc, NULL}, {"translation", @@ -3141,7 +3217,12 @@ static PyGetSetDef Matrix_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/*-----------------------METHOD DEFINITIONS ----------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Method Definitions + * \{ */ + static struct PyMethodDef Matrix_methods[] = { /* Derived values. */ {"determinant", (PyCFunction)Matrix_determinant, METH_NOARGS, Matrix_determinant_doc}, @@ -3205,7 +3286,12 @@ static struct PyMethodDef Matrix_methods[] = { {NULL, NULL, 0, NULL}, }; -/*------------------PY_OBECT DEFINITION--------------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: Python Object Definition + * \{ */ + PyDoc_STRVAR( matrix_doc, ".. class:: Matrix([rows])\n" @@ -3268,6 +3354,12 @@ PyTypeObject matrix_Type = { NULL, /*tp_del*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: C/API Constructors + * \{ */ + PyObject *Matrix_CreatePyObject(const float *mat, const ushort col_num, const ushort row_num, @@ -3380,6 +3472,12 @@ PyObject *Matrix_CreatePyObject_alloc(float *mat, return (PyObject *)self; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix Type: C/API Parse Utilities + * \{ */ + /** * Use with PyArg_ParseTuple's "O&" formatting. */ @@ -3460,8 +3558,11 @@ int Matrix_Parse4x4(PyObject *o, void *p) return 1; } -/* ---------------------------------------------------------------------------- - * special type for alternate access */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Struct & Internal Functions + * \{ */ typedef struct { PyObject_HEAD /* Required Python macro. */ @@ -3491,7 +3592,11 @@ static void MatrixAccess_dealloc(MatrixAccessObject *self) Py_TYPE(self)->tp_free(self); } -/* sequence access */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Sequence Protocol + * \{ */ static int MatrixAccess_len(MatrixAccessObject *self) { @@ -3609,13 +3714,13 @@ static int MatrixAccess_ass_subscript(MatrixAccessObject *self, PyObject *item, static PyObject *MatrixAccess_iter(MatrixAccessObject *self) { - /* Try get values from a collection */ + /* Try get values from a collection. */ PyObject *ret; PyObject *iter = NULL; ret = MatrixAccess_slice(self, 0, MATRIX_MAX_DIM); - /* we know this is a tuple so no need to PyIter_Check - * otherwise it could be NULL (unlikely) if conversion failed */ + /* We know this is a tuple so no need to #PyIter_Check + * otherwise it could be NULL (unlikely) if conversion failed. */ if (ret) { iter = PyObject_GetIter(ret); Py_DECREF(ret); @@ -3630,6 +3735,12 @@ static PyMappingMethods MatrixAccess_AsMapping = { (objobjargproc)MatrixAccess_ass_subscript, }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: Python Object Definition + * \{ */ + PyTypeObject matrix_access_Type = { PyVarObject_HEAD_INIT(NULL, 0) "MatrixAccess", /*tp_name*/ sizeof(MatrixAccessObject), /*tp_basicsize*/ @@ -3658,6 +3769,12 @@ PyTypeObject matrix_access_Type = { (getiterfunc)MatrixAccess_iter, /* getiterfunc tp_iter; */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Matrix-Access Type: C/API Constructor + * \{ */ + static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrixAccess_t type) { MatrixAccessObject *matrix_access = (MatrixAccessObject *)PyObject_GC_New(MatrixObject, @@ -3671,5 +3788,4 @@ static PyObject *MatrixAccess_CreatePyObject(MatrixObject *matrix, const eMatrix return (PyObject *)matrix_access; } -/* end special access - * -------------------------------------------------------------------------- */ +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Matrix.h b/source/blender/python/mathutils/mathutils_Matrix.h index bc596ce6ac8..c8c207c13f3 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.h +++ b/source/blender/python/mathutils/mathutils_Matrix.h @@ -45,7 +45,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * blender (stored in blend_data). This is an either/or struct not both */ -/* prototypes */ +/* Prototypes. */ + PyObject *Matrix_CreatePyObject(const float *mat, ushort col_num, ushort row_num, @@ -70,6 +71,7 @@ PyObject *Matrix_CreatePyObject_alloc(float *mat, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; /* PyArg_ParseTuple's "O&" formatting helpers. */ + int Matrix_ParseAny(PyObject *o, void *p); int Matrix_Parse2x2(PyObject *o, void *p); int Matrix_Parse3x3(PyObject *o, void *p); diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 7b51154f0d0..6994a313237 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -26,9 +26,49 @@ static void quat__axis_angle_sanitize(float axis[3], float *angle); static PyObject *Quaternion_copy(QuaternionObject *self); static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args); -/* -----------------------------METHODS------------------------------ */ +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ +static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *), + QuaternionObject *self) +{ + PyObject *ret = Quaternion_copy(self); + PyObject *ret_dummy = quat_func((QuaternionObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return ret; + } + /* error */ + Py_DECREF(ret); + return NULL; +} + +/** Axis vector suffers from precision errors, use this function to ensure. */ +static void quat__axis_angle_sanitize(float axis[3], float *angle) +{ + if (axis) { + if (is_zero_v3(axis) || !isfinite(axis[0]) || !isfinite(axis[1]) || !isfinite(axis[2])) { + axis[0] = 1.0f; + axis[1] = 0.0f; + axis[2] = 0.0f; + } + else if (EXPP_FloatsAreEqual(axis[0], 0.0f, 10) && EXPP_FloatsAreEqual(axis[1], 0.0f, 10) && + EXPP_FloatsAreEqual(axis[2], 0.0f, 10)) { + axis[0] = 1.0f; + } + } + + if (angle) { + if (!isfinite(*angle)) { + *angle = 0.0f; + } + } +} + +/** + * \note #BaseMath_ReadCallback must be called beforehand. + */ static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits) { PyObject *ret; @@ -50,6 +90,72 @@ static PyObject *Quaternion_to_tuple_ext(QuaternionObject *self, int ndigits) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: `__new__` / `mathutils.Quaternion()` + * \{ */ + +static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *seq = NULL; + double angle = 0.0f; + float quat[QUAT_SIZE]; + unit_qt(quat); + + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "mathutils.Quaternion(): " + "takes no keyword args"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) { + return NULL; + } + + switch (PyTuple_GET_SIZE(args)) { + case 0: + break; + case 1: { + int size; + + if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == + -1) { + return NULL; + } + + if (size == 4) { + /* 4d: Quaternion (common case) */ + } + else { + /* 3d: Interpret as exponential map */ + BLI_assert(size == 3); + expmap_to_quat(quat, quat); + } + + break; + } + case 2: { + float axis[3]; + if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) { + return NULL; + } + angle = angle_wrap_rad(angle); /* clamp because of precision issues */ + axis_angle_to_quat(quat, axis, angle); + break; + /* PyArg_ParseTuple assures no more than 2 */ + } + } + return Quaternion_CreatePyObject(quat, type); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Euler + * \{ */ + PyDoc_STRVAR(Quaternion_to_euler_doc, ".. method:: to_euler(order, euler_compat)\n" "\n" @@ -114,6 +220,12 @@ static PyObject *Quaternion_to_euler(QuaternionObject *self, PyObject *args) return Euler_CreatePyObject(eul, order, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Matrix + * \{ */ + PyDoc_STRVAR(Quaternion_to_matrix_doc, ".. method:: to_matrix()\n" "\n" @@ -133,6 +245,12 @@ static PyObject *Quaternion_to_matrix(QuaternionObject *self) return Matrix_CreatePyObject(mat, 3, 3, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Axis/Angle + * \{ */ + PyDoc_STRVAR(Quaternion_to_axis_angle_doc, ".. method:: to_axis_angle()\n" "\n" @@ -163,6 +281,12 @@ static PyObject *Quaternion_to_axis_angle(QuaternionObject *self) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Swing/Twist + * \{ */ + PyDoc_STRVAR(Quaternion_to_swing_twist_doc, ".. method:: to_swing_twist(axis)\n" "\n" @@ -207,6 +331,12 @@ static PyObject *Quaternion_to_swing_twist(QuaternionObject *self, PyObject *axi return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: To Exponential Map + * \{ */ + PyDoc_STRVAR( Quaternion_to_exponential_map_doc, ".. method:: to_exponential_map()\n" @@ -232,6 +362,12 @@ static PyObject *Quaternion_to_exponential_map(QuaternionObject *self) return Vector_CreatePyObject(expmap, 3, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Cross Product + * \{ */ + PyDoc_STRVAR(Quaternion_cross_doc, ".. method:: cross(other)\n" "\n" @@ -259,6 +395,12 @@ static PyObject *Quaternion_cross(QuaternionObject *self, PyObject *value) return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Dot Product + * \{ */ + PyDoc_STRVAR(Quaternion_dot_doc, ".. method:: dot(other)\n" "\n" @@ -285,6 +427,12 @@ static PyObject *Quaternion_dot(QuaternionObject *self, PyObject *value) return PyFloat_FromDouble(dot_qtqt(self->quat, tquat)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Rotation Difference + * \{ */ + PyDoc_STRVAR(Quaternion_rotation_difference_doc, ".. function:: rotation_difference(other)\n" "\n" @@ -315,6 +463,12 @@ static PyObject *Quaternion_rotation_difference(QuaternionObject *self, PyObject return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Spherical Interpolation (slerp) + * \{ */ + PyDoc_STRVAR(Quaternion_slerp_doc, ".. function:: slerp(other, factor)\n" "\n" @@ -360,6 +514,12 @@ static PyObject *Quaternion_slerp(QuaternionObject *self, PyObject *args) return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Rotate + * \{ */ + PyDoc_STRVAR(Quaternion_rotate_doc, ".. method:: rotate(other)\n" "\n" @@ -423,9 +583,15 @@ static PyObject *Quaternion_make_compatible(QuaternionObject *self, PyObject *va Py_RETURN_NONE; } -/* ----------------------------Quaternion.normalize()---------------- */ -/* Normalize the quaternion. This may change the angle as well as the - * rotation axis, as all of (w, x, y, z) are scaled. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Normalize + * + * Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. + * \{ */ + PyDoc_STRVAR(Quaternion_normalize_doc, ".. function:: normalize()\n" "\n" @@ -453,6 +619,15 @@ static PyObject *Quaternion_normalized(QuaternionObject *self) return quat__apply_to_copy(Quaternion_normalize, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Invert + * + * Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. + * \{ */ + PyDoc_STRVAR(Quaternion_invert_doc, ".. function:: invert()\n" "\n" @@ -480,6 +655,12 @@ static PyObject *Quaternion_inverted(QuaternionObject *self) return quat__apply_to_copy(Quaternion_invert, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Set Identity + * \{ */ + PyDoc_STRVAR(Quaternion_identity_doc, ".. function:: identity()\n" "\n" @@ -498,6 +679,12 @@ static PyObject *Quaternion_identity(QuaternionObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Negate + * \{ */ + PyDoc_STRVAR(Quaternion_negate_doc, ".. function:: negate()\n" "\n" @@ -516,6 +703,12 @@ static PyObject *Quaternion_negate(QuaternionObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Conjugate + * \{ */ + PyDoc_STRVAR(Quaternion_conjugate_doc, ".. function:: conjugate()\n" "\n" @@ -543,6 +736,12 @@ static PyObject *Quaternion_conjugated(QuaternionObject *self) return quat__apply_to_copy(Quaternion_conjugate, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Methods: Copy/Deep-Copy + * \{ */ + PyDoc_STRVAR(Quaternion_copy_doc, ".. function:: copy()\n" "\n" @@ -569,7 +768,12 @@ static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args) return Quaternion_copy(self); } -/* print the object to screen */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Quaternion_repr(QuaternionObject *self) { PyObject *ret, *tuple; @@ -608,6 +812,12 @@ static PyObject *Quaternion_str(QuaternionObject *self) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Rich Compare + * \{ */ + static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op) { PyObject *res; @@ -646,6 +856,12 @@ static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op) return Py_INCREF_RET(res); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Hash (`__hash__`) + * \{ */ + static Py_hash_t Quaternion_hash(QuaternionObject *self) { if (BaseMath_ReadCallback(self) == -1) { @@ -659,15 +875,19 @@ static Py_hash_t Quaternion_hash(QuaternionObject *self) return mathutils_array_hash(self->quat, QUAT_SIZE); } -/* ---------------------SEQUENCE PROTOCOLS------------------------ */ -/* ----------------------------len(object)------------------------ */ -/* sequence length */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Quaternion_len(QuaternionObject *UNUSED(self)) { return QUAT_SIZE; } -/* ----------------------------object[]--------------------------- */ -/* sequence accessor (get) */ + +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Quaternion_item(QuaternionObject *self, int i) { if (i < 0) { @@ -687,8 +907,8 @@ static PyObject *Quaternion_item(QuaternionObject *self, int i) return PyFloat_FromDouble(self->quat[i]); } -/* ----------------------------object[]------------------------- */ -/* sequence accessor (set) */ + +/** Sequence accessor (set): `object[i] = x`. */ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) { float f; @@ -724,8 +944,8 @@ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) return 0; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (get) */ + +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end) { PyObject *tuple; @@ -749,8 +969,8 @@ static PyObject *Quaternion_slice(QuaternionObject *self, int begin, int end) return tuple; } -/* ----------------------------object[z:y]------------------------ */ -/* sequence slice (set) */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyObject *seq) { int i, size; @@ -779,7 +999,7 @@ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyOb return -1; } - /* parsed well - now set in vector */ + /* Parsed well, now set in vector. */ for (i = 0; i < size; i++) { self->quat[begin + i] = quat[i]; } @@ -788,6 +1008,7 @@ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyOb return 0; } +/** Sequence generic subscript (get): `x = object[...]`. */ static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item) { if (PyIndex_Check(item)) { @@ -824,6 +1045,7 @@ static PyObject *Quaternion_subscript(QuaternionObject *self, PyObject *item) return NULL; } +/** Sequence generic subscript (set): `object[...] = x`. */ static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyObject *value) { if (PyIndex_Check(item)) { @@ -856,9 +1078,13 @@ static int Quaternion_ass_subscript(QuaternionObject *self, PyObject *item, PyOb return -1; } -/* ------------------------NUMERIC PROTOCOLS---------------------- */ -/* ------------------------obj + obj------------------------------ */ -/* addition */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Quaternion_add(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -882,8 +1108,8 @@ static PyObject *Quaternion_add(PyObject *q1, PyObject *q2) add_qt_qtqt(quat, quat1->quat, quat2->quat, 1.0f); return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); } -/* ------------------------obj - obj------------------------------ */ -/* subtraction */ + +/** Subtraction: `object - object`. */ static PyObject *Quaternion_sub(PyObject *q1, PyObject *q2) { int x; @@ -921,8 +1147,7 @@ static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar) return Quaternion_CreatePyObject(tquat, Py_TYPE(quat)); } -/*------------------------obj * obj------------------------------ - * multiplication */ +/** Multiplication (element-wise or scalar): `object * object`. */ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) { float scalar; @@ -965,8 +1190,8 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) Py_TYPE(q2)->tp_name); return NULL; } -/*------------------------obj *= obj------------------------------ - * in-place multiplication */ + +/** Multiplication in-place (element-wise or scalar): `object *= object`. */ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2) { float scalar; @@ -1005,8 +1230,8 @@ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2) Py_INCREF(q1); return q1; } -/*------------------------obj @ obj------------------------------ - * quaternion multiplication */ + +/** Multiplication (quaternion multiply): `object @ object`. */ static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -1060,8 +1285,8 @@ static PyObject *Quaternion_matmul(PyObject *q1, PyObject *q2) Py_TYPE(q2)->tp_name); return NULL; } -/*------------------------obj @= obj------------------------------ - * in-place quaternion multiplication */ + +/** Multiplication in-place (quaternion multiply): `object @= object`. */ static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2) { float quat[QUAT_SIZE]; @@ -1098,8 +1323,7 @@ static PyObject *Quaternion_imatmul(PyObject *q1, PyObject *q2) return q1; } -/* -obj - * Returns the negative of this object. */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Quaternion_neg(QuaternionObject *self) { float tquat[QUAT_SIZE]; @@ -1112,18 +1336,23 @@ static PyObject *Quaternion_neg(QuaternionObject *self) return Quaternion_CreatePyObject(tquat, Py_TYPE(self)); } -/* -----------------PROTOCOL DECLARATIONS-------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Protocol Declarations + * \{ */ + static PySequenceMethods Quaternion_SeqMethods = { - (lenfunc)Quaternion_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Quaternion_item, /* sq_item */ - (ssizessizeargfunc)NULL, /* sq_slice, deprecated */ - (ssizeobjargproc)Quaternion_ass_item, /* sq_ass_item */ - (ssizessizeobjargproc)NULL, /* sq_ass_slice, deprecated */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Quaternion_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Quaternion_item, /*sq_item*/ + (ssizessizeargfunc)NULL, /*sq_slice(deprecated)*/ + (ssizeobjargproc)Quaternion_ass_item, /*sq_ass_item*/ + (ssizessizeobjargproc)NULL, /*sq_ass_slice(deprecated)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat*/ + (ssizeargfunc)NULL, /*sq_inplace_repeat*/ }; static PyMappingMethods Quaternion_AsMapping = { @@ -1152,25 +1381,31 @@ static PyNumberMethods Quaternion_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - NULL, /* nb_inplace_add */ - NULL, /* nb_inplace_subtract */ - (binaryfunc)Quaternion_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - NULL, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - NULL, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Quaternion_matmul, /* nb_matrix_multiply */ - (binaryfunc)Quaternion_imatmul, /* nb_inplace_matrix_multiply */ + NULL, /*nb_inplace_add*/ + NULL, /*nb_inplace_subtract*/ + (binaryfunc)Quaternion_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + NULL, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + NULL, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Quaternion_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Quaternion_imatmul, /*nb_inplace_matrix_multiply*/ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Get/Set Item Implementation + * \{ */ + PyDoc_STRVAR(Quaternion_axis_doc, "Quaternion axis value.\n\n:type: float"); static PyObject *Quaternion_axis_get(QuaternionObject *self, void *type) { @@ -1300,98 +1535,69 @@ static int Quaternion_axis_vector_set(QuaternionObject *self, return 0; } -/* ----------------------------------mathutils.Quaternion() -------------- */ -static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *seq = NULL; - double angle = 0.0f; - float quat[QUAT_SIZE]; - unit_qt(quat); - - if (kwds && PyDict_Size(kwds)) { - PyErr_SetString(PyExc_TypeError, - "mathutils.Quaternion(): " - "takes no keyword args"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "|Od:mathutils.Quaternion", &seq, &angle)) { - return NULL; - } - - switch (PyTuple_GET_SIZE(args)) { - case 0: - break; - case 1: { - int size; - - if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == - -1) { - return NULL; - } +/** \} */ - if (size == 4) { - /* 4d: Quaternion (common case) */ - } - else { - /* 3d: Interpret as exponential map */ - BLI_assert(size == 3); - expmap_to_quat(quat, quat); - } - - break; - } - case 2: { - float axis[3]; - if (mathutils_array_parse(axis, 3, 3, seq, "mathutils.Quaternion()") == -1) { - return NULL; - } - angle = angle_wrap_rad(angle); /* clamp because of precision issues */ - axis_angle_to_quat(quat, axis, angle); - break; - /* PyArg_ParseTuple assures no more than 2 */ - } - } - return Quaternion_CreatePyObject(quat, type); -} +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Get/Set Item Definitions + * \{ */ -static PyObject *quat__apply_to_copy(PyObject *(*quat_func)(QuaternionObject *), - QuaternionObject *self) -{ - PyObject *ret = Quaternion_copy(self); - PyObject *ret_dummy = quat_func((QuaternionObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return ret; - } - /* error */ - Py_DECREF(ret); - return NULL; -} +static PyGetSetDef Quaternion_getseters[] = { + {"w", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)0}, + {"x", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)1}, + {"y", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)2}, + {"z", + (getter)Quaternion_axis_get, + (setter)Quaternion_axis_set, + Quaternion_axis_doc, + (void *)3}, + {"magnitude", (getter)Quaternion_magnitude_get, (setter)NULL, Quaternion_magnitude_doc, NULL}, + {"angle", + (getter)Quaternion_angle_get, + (setter)Quaternion_angle_set, + Quaternion_angle_doc, + NULL}, + {"axis", + (getter)Quaternion_axis_vector_get, + (setter)Quaternion_axis_vector_set, + Quaternion_axis_vector_doc, + NULL}, + {"is_wrapped", + (getter)BaseMathObject_is_wrapped_get, + (setter)NULL, + BaseMathObject_is_wrapped_doc, + NULL}, + {"is_frozen", + (getter)BaseMathObject_is_frozen_get, + (setter)NULL, + BaseMathObject_is_frozen_doc, + NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, + {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, + {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ +}; -/* axis vector suffers from precision errors, use this function to ensure */ -static void quat__axis_angle_sanitize(float axis[3], float *angle) -{ - if (axis) { - if (is_zero_v3(axis) || !isfinite(axis[0]) || !isfinite(axis[1]) || !isfinite(axis[2])) { - axis[0] = 1.0f; - axis[1] = 0.0f; - axis[2] = 0.0f; - } - else if (EXPP_FloatsAreEqual(axis[0], 0.0f, 10) && EXPP_FloatsAreEqual(axis[1], 0.0f, 10) && - EXPP_FloatsAreEqual(axis[2], 0.0f, 10)) { - axis[0] = 1.0f; - } - } +/** \} */ - if (angle) { - if (!isfinite(*angle)) { - *angle = 0.0f; - } - } -} +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Method Definitions + * \{ */ -/* -----------------------METHOD DEFINITIONS ---------------------- */ static struct PyMethodDef Quaternion_methods[] = { /* In place only. */ {"identity", (PyCFunction)Quaternion_identity, METH_NOARGS, Quaternion_identity_doc}, @@ -1446,61 +1652,12 @@ static struct PyMethodDef Quaternion_methods[] = { {NULL, NULL, 0, NULL}, }; -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ -static PyGetSetDef Quaternion_getseters[] = { - {"w", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)0}, - {"x", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)1}, - {"y", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)2}, - {"z", - (getter)Quaternion_axis_get, - (setter)Quaternion_axis_set, - Quaternion_axis_doc, - (void *)3}, - {"magnitude", (getter)Quaternion_magnitude_get, (setter)NULL, Quaternion_magnitude_doc, NULL}, - {"angle", - (getter)Quaternion_angle_get, - (setter)Quaternion_angle_set, - Quaternion_angle_doc, - NULL}, - {"axis", - (getter)Quaternion_axis_vector_get, - (setter)Quaternion_axis_vector_set, - Quaternion_axis_vector_doc, - NULL}, - {"is_wrapped", - (getter)BaseMathObject_is_wrapped_get, - (setter)NULL, - BaseMathObject_is_wrapped_doc, - NULL}, - {"is_frozen", - (getter)BaseMathObject_is_frozen_get, - (setter)NULL, - BaseMathObject_is_frozen_doc, - NULL}, - {"is_valid", - (getter)BaseMathObject_is_valid_get, - (setter)NULL, - BaseMathObject_is_valid_doc, - NULL}, - {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, - {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ -}; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: Python Object Definition + * \{ */ -/* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(quaternion_doc, ".. class:: Quaternion([seq, [angle]])\n" "\n" @@ -1577,6 +1734,12 @@ PyTypeObject quaternion_Type = { NULL, /* tp_del */ }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Quaternion Type: C/API Constructors + * \{ */ + PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type) { QuaternionObject *self; @@ -1643,3 +1806,5 @@ PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, uchar cb_type, uchar c return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Quaternion.h b/source/blender/python/mathutils/mathutils_Quaternion.h index 96e3d2e0055..c45b0a98a7b 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.h +++ b/source/blender/python/mathutils/mathutils_Quaternion.h @@ -20,7 +20,8 @@ typedef struct { * be stored in py_data) or be a wrapper for data allocated through * blender (stored in blend_data). This is an either/or struct not both */ -/* prototypes */ +/* Prototypes. */ + PyObject *Quaternion_CreatePyObject(const float quat[4], PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; PyObject *Quaternion_CreatePyObject_wrap(float quat[4], diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index ffeb121b3d1..0c9cbd6ccfa 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -24,19 +24,108 @@ */ #define MAX_DIMENSIONS 4 -/* Swizzle axes get packed into a single value that is used as a closure. Each +/** + * Swizzle axes get packed into a single value that is used as a closure. Each * axis uses SWIZZLE_BITS_PER_AXIS bits. The first bit (SWIZZLE_VALID_AXIS) is - * used as a sentinel: if it is unset, the axis is not valid. */ + * used as a sentinel: if it is unset, the axis is not valid. + */ #define SWIZZLE_BITS_PER_AXIS 3 #define SWIZZLE_VALID_AXIS 0x4 #define SWIZZLE_AXIS 0x3 static PyObject *Vector_copy(VectorObject *self); static PyObject *Vector_deepcopy(VectorObject *self, PyObject *args); -static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits); + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/** + * Row vector multiplication - (Vector * Matrix) + * <pre> + * [x][y][z] * [1][4][7] + * [2][5][8] + * [3][6][9] + * </pre> + * \note vector/matrix multiplication is not commutative. + */ static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], VectorObject *vec, - MatrixObject *mat); + MatrixObject *mat) +{ + float vec_cpy[MAX_DIMENSIONS]; + int row, col, z = 0, vec_num = vec->vec_num; + + if (mat->row_num != vec_num) { + if (mat->row_num == 4 && vec_num == 3) { + vec_cpy[3] = 1.0f; + } + else { + PyErr_SetString(PyExc_ValueError, + "vector * matrix: matrix column size " + "and the vector size must be the same"); + return -1; + } + } + + if (BaseMath_ReadCallback(vec) == -1 || BaseMath_ReadCallback(mat) == -1) { + return -1; + } + + memcpy(vec_cpy, vec->vec, vec_num * sizeof(float)); + + r_vec[3] = 1.0f; + /* Multiplication. */ + for (col = 0; col < mat->col_num; col++) { + double dot = 0.0; + for (row = 0; row < mat->row_num; row++) { + dot += (double)(MATRIX_ITEM(mat, row, col) * vec_cpy[row]); + } + r_vec[z++] = (float)dot; + } + return 0; +} + +static PyObject *vec__apply_to_copy(PyObject *(*vec_func)(VectorObject *), VectorObject *self) +{ + PyObject *ret = Vector_copy(self); + PyObject *ret_dummy = vec_func((VectorObject *)ret); + if (ret_dummy) { + Py_DECREF(ret_dummy); + return (PyObject *)ret; + } + /* error */ + Py_DECREF(ret); + return NULL; +} + +/** \note #BaseMath_ReadCallback must be called beforehand. */ +static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits) +{ + PyObject *ret; + int i; + + ret = PyTuple_New(self->vec_num); + + if (ndigits >= 0) { + for (i = 0; i < self->vec_num; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->vec[i], ndigits))); + } + } + else { + for (i = 0; i < self->vec_num; i++) { + PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->vec[i])); + } + } + + return ret; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: `__new__` / `mathutils.Vector()` + * \{ */ /** * Supports 2D, 3D, and 4D vector objects both int and float values @@ -82,20 +171,12 @@ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return Vector_CreatePyObject_alloc(vec, vec_num, type); } -static PyObject *vec__apply_to_copy(PyObject *(*vec_func)(VectorObject *), VectorObject *self) -{ - PyObject *ret = Vector_copy(self); - PyObject *ret_dummy = vec_func((VectorObject *)ret); - if (ret_dummy) { - Py_DECREF(ret_dummy); - return (PyObject *)ret; - } - /* error */ - Py_DECREF(ret); - return NULL; -} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Class Methods + * \{ */ -/*-----------------------CLASS-METHODS----------------------------*/ PyDoc_STRVAR(C_Vector_Fill_doc, ".. classmethod:: Fill(size, fill=0.0)\n" "\n" @@ -311,7 +392,12 @@ static PyObject *C_Vector_Repeat(PyObject *cls, PyObject *args) return Vector_CreatePyObject_alloc(vec, vec_num, (PyTypeObject *)cls); } -/*-----------------------------METHODS---------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Zero + * \{ */ + PyDoc_STRVAR(Vector_zero_doc, ".. method:: zero()\n" "\n" @@ -331,6 +417,12 @@ static PyObject *Vector_zero(VectorObject *self) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Normalize + * \{ */ + PyDoc_STRVAR(Vector_normalize_doc, ".. method:: normalize()\n" "\n" @@ -364,6 +456,12 @@ static PyObject *Vector_normalized(VectorObject *self) return vec__apply_to_copy(Vector_normalize, self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Resize + * \{ */ + PyDoc_STRVAR(Vector_resize_doc, ".. method:: resize(size=3)\n" "\n" @@ -553,6 +651,13 @@ static PyObject *Vector_resize_4d(VectorObject *self) self->vec_num = 4; Py_RETURN_NONE; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To N-dimensions + * \{ */ + PyDoc_STRVAR(Vector_to_2d_doc, ".. method:: to_2d()\n" "\n" @@ -605,6 +710,12 @@ static PyObject *Vector_to_4d(VectorObject *self) return Vector_CreatePyObject(tvec, 4, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To Tuple + * \{ */ + PyDoc_STRVAR(Vector_to_tuple_doc, ".. method:: to_tuple(precision=-1)\n" "\n" @@ -614,28 +725,6 @@ PyDoc_STRVAR(Vector_to_tuple_doc, " :type precision: int\n" " :return: the values of the vector rounded by *precision*\n" " :rtype: tuple\n"); -/* NOTE: BaseMath_ReadCallback must be called beforehand. */ -static PyObject *Vector_to_tuple_ex(VectorObject *self, int ndigits) -{ - PyObject *ret; - int i; - - ret = PyTuple_New(self->vec_num); - - if (ndigits >= 0) { - for (i = 0; i < self->vec_num; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(double_round((double)self->vec[i], ndigits))); - } - } - else { - for (i = 0; i < self->vec_num; i++) { - PyTuple_SET_ITEM(ret, i, PyFloat_FromDouble(self->vec[i])); - } - } - - return ret; -} - static PyObject *Vector_to_tuple(VectorObject *self, PyObject *args) { int ndigits = 0; @@ -662,6 +751,12 @@ static PyObject *Vector_to_tuple(VectorObject *self, PyObject *args) return Vector_to_tuple_ex(self, ndigits); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: To Track Quaternion + * \{ */ + PyDoc_STRVAR(Vector_to_track_quat_doc, ".. method:: to_track_quat(track, up)\n" "\n" @@ -781,6 +876,12 @@ static PyObject *Vector_to_track_quat(VectorObject *self, PyObject *args) return Quaternion_CreatePyObject(quat, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Orthogonal + * \{ */ + PyDoc_STRVAR( Vector_orthogonal_doc, ".. method:: orthogonal()\n" @@ -816,12 +917,15 @@ static PyObject *Vector_orthogonal(VectorObject *self) return Vector_CreatePyObject(vec, self->vec_num, Py_TYPE(self)); } -/** - * Vector.reflect(mirror): return a reflected vector on the mirror normal. - * <pre> - * vec - ((2 * dot(vec, mirror)) * mirror) - * </pre> - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Reflect + * + * `Vector.reflect(mirror)`: return a reflected vector on the mirror normal: + * `vec - ((2 * dot(vec, mirror)) * mirror)`. + * \{ */ + PyDoc_STRVAR(Vector_reflect_doc, ".. method:: reflect(mirror)\n" "\n" @@ -866,6 +970,12 @@ static PyObject *Vector_reflect(VectorObject *self, PyObject *value) return Vector_CreatePyObject(reflect, self->vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Cross Product + * \{ */ + PyDoc_STRVAR(Vector_cross_doc, ".. method:: cross(other)\n" "\n" @@ -908,6 +1018,12 @@ static PyObject *Vector_cross(VectorObject *self, PyObject *value) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Dot Product + * \{ */ + PyDoc_STRVAR(Vector_dot_doc, ".. method:: dot(other)\n" "\n" @@ -936,6 +1052,12 @@ static PyObject *Vector_dot(VectorObject *self, PyObject *value) return ret; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Angle + * \{ */ + PyDoc_STRVAR( Vector_angle_doc, ".. function:: angle(other, fallback=None)\n" @@ -1001,6 +1123,12 @@ static PyObject *Vector_angle(VectorObject *self, PyObject *args) return PyFloat_FromDouble(saacos(dot / (sqrt(dot_self) * sqrt(dot_other)))); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Angle Signed + * \{ */ + PyDoc_STRVAR( Vector_angle_signed_doc, ".. function:: angle_signed(other, fallback)\n" @@ -1055,6 +1183,12 @@ static PyObject *Vector_angle_signed(VectorObject *self, PyObject *args) return PyFloat_FromDouble(angle_signed_v2v2(self->vec, tvec)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Rotation Difference + * \{ */ + PyDoc_STRVAR(Vector_rotation_difference_doc, ".. function:: rotation_difference(other)\n" "\n" @@ -1096,6 +1230,12 @@ static PyObject *Vector_rotation_difference(VectorObject *self, PyObject *value) return Quaternion_CreatePyObject(quat, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Project + * \{ */ + PyDoc_STRVAR(Vector_project_doc, ".. function:: project(other)\n" "\n" @@ -1134,6 +1274,12 @@ static PyObject *Vector_project(VectorObject *self, PyObject *value) return Vector_CreatePyObject_alloc(tvec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Linear Interpolation + * \{ */ + PyDoc_STRVAR(Vector_lerp_doc, ".. function:: lerp(other, factor)\n" "\n" @@ -1170,6 +1316,12 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) return Vector_CreatePyObject_alloc(tvec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Spherical Interpolation + * \{ */ + PyDoc_STRVAR(Vector_slerp_doc, ".. function:: slerp(other, factor, fallback=None)\n" "\n" @@ -1256,6 +1408,12 @@ static PyObject *Vector_slerp(VectorObject *self, PyObject *args) return Vector_CreatePyObject(ret_vec, vec_num, Py_TYPE(self)); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Rotate + * \{ */ + PyDoc_STRVAR( Vector_rotate_doc, ".. function:: rotate(other)\n" @@ -1297,6 +1455,34 @@ static PyObject *Vector_rotate(VectorObject *self, PyObject *value) Py_RETURN_NONE; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Negate + * \{ */ + +PyDoc_STRVAR(Vector_negate_doc, + ".. method:: negate()\n" + "\n" + " Set all values to their negative.\n"); +static PyObject *Vector_negate(VectorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) { + return NULL; + } + + negate_vn(self->vec, self->vec_num); + + (void)BaseMath_WriteCallback(self); /* already checked for error */ + Py_RETURN_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Methods: Copy/Deep-Copy + * \{ */ + PyDoc_STRVAR(Vector_copy_doc, ".. function:: copy()\n" "\n" @@ -1323,6 +1509,12 @@ static PyObject *Vector_deepcopy(VectorObject *self, PyObject *args) return Vector_copy(self); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: `__repr__` & `__str__` + * \{ */ + static PyObject *Vector_repr(VectorObject *self) { PyObject *ret, *tuple; @@ -1362,13 +1554,124 @@ static PyObject *Vector_str(VectorObject *self) } #endif -/* Sequence Protocol */ -/* sequence length len(vector) */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Rich Compare + * \{ */ + +static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) +{ + VectorObject *vecA = NULL, *vecB = NULL; + int result = 0; + const double epsilon = 0.000001f; + double lenA, lenB; + + if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { + if (comparison_type == Py_NE) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; + } + vecA = (VectorObject *)objectA; + vecB = (VectorObject *)objectB; + + if (BaseMath_ReadCallback(vecA) == -1 || BaseMath_ReadCallback(vecB) == -1) { + return NULL; + } + + if (vecA->vec_num != vecB->vec_num) { + if (comparison_type == Py_NE) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; + } + + switch (comparison_type) { + case Py_LT: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA < lenB) { + result = 1; + } + break; + case Py_LE: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA < lenB) { + result = 1; + } + else { + result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); + } + break; + case Py_EQ: + result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); + break; + case Py_NE: + result = !EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); + break; + case Py_GT: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA > lenB) { + result = 1; + } + break; + case Py_GE: + lenA = len_squared_vn(vecA->vec, vecA->vec_num); + lenB = len_squared_vn(vecB->vec, vecB->vec_num); + if (lenA > lenB) { + result = 1; + } + else { + result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); + } + break; + default: + printf("The result of the comparison could not be evaluated"); + break; + } + if (result == 1) { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Hash (`__hash__`) + * \{ */ + +static Py_hash_t Vector_hash(VectorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) { + return -1; + } + + if (BaseMathObject_Prepare_ForHash(self) == -1) { + return -1; + } + + return mathutils_array_hash(self->vec, self->vec_num); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Sequence & Mapping Protocols Implementation + * \{ */ + +/** Sequence length: `len(object)`. */ static int Vector_len(VectorObject *self) { return self->vec_num; } -/* sequence accessor (get): vector[index] */ + static PyObject *vector_item_internal(VectorObject *self, int i, const bool is_attr) { if (i < 0) { @@ -1395,11 +1698,12 @@ static PyObject *vector_item_internal(VectorObject *self, int i, const bool is_a return PyFloat_FromDouble(self->vec[i]); } +/** Sequence accessor (get): `x = object[i]`. */ static PyObject *Vector_item(VectorObject *self, int i) { return vector_item_internal(self, i, false); } -/* sequence accessor (set): vector[index] = value */ + static int vector_ass_item_internal(VectorObject *self, int i, PyObject *value, const bool is_attr) { float scalar; @@ -1442,12 +1746,13 @@ static int vector_ass_item_internal(VectorObject *self, int i, PyObject *value, return 0; } +/** Sequence accessor (set): `object[i] = x`. */ static int Vector_ass_item(VectorObject *self, int i, PyObject *value) { return vector_ass_item_internal(self, i, value, false); } -/* sequence slice (get): vector[a:b] */ +/** Sequence slice accessor (get): `x = object[i:j]`. */ static PyObject *Vector_slice(VectorObject *self, int begin, int end) { PyObject *tuple; @@ -1471,7 +1776,8 @@ static PyObject *Vector_slice(VectorObject *self, int begin, int end) return tuple; } -/* sequence slice (set): vector[a:b] = value */ + +/** Sequence slice accessor (set): `object[i:j] = x`. */ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *seq) { int vec_num = 0; @@ -1509,8 +1815,83 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se return 0; } -/* Numeric Protocols */ -/* addition: obj + obj */ +/** Sequence generic subscript (get): `x = object[...]`. */ +static PyObject *Vector_subscript(VectorObject *self, PyObject *item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return NULL; + } + if (i < 0) { + i += self->vec_num; + } + return Vector_item(self, i); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyTuple_New(0); + } + if (step == 1) { + return Vector_slice(self, start, stop); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); + return NULL; + } + + PyErr_Format( + PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return NULL; +} + +/** Sequence generic subscript (set): `object[...] = x`. */ +static int Vector_ass_subscript(VectorObject *self, PyObject *item, PyObject *value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + if (i < 0) { + i += self->vec_num; + } + return Vector_ass_item(self, i, value); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + if (step == 1) { + return Vector_ass_slice(self, start, stop, value); + } + + PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); + return -1; + } + + PyErr_Format( + PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); + return -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Numeric Protocol Implementation + * \{ */ + +/** Addition: `object + object`. */ static PyObject *Vector_add(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1552,7 +1933,7 @@ static PyObject *Vector_add(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* addition in-place: obj += obj */ +/** Addition in-place: `object += object`. */ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1586,7 +1967,7 @@ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) return v1; } -/* subtraction: obj - obj */ +/** Subtraction: `object - object`. */ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1627,7 +2008,7 @@ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* subtraction in-place: obj -= obj */ +/** Subtraction in-place: `object -= object`. */ static PyObject *Vector_isub(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1661,8 +2042,7 @@ static PyObject *Vector_isub(PyObject *v1, PyObject *v2) return v1; } -/*------------------------obj * obj------------------------------ - * multiplication */ +/* Multiply internal implementation `object * object`, `object *= object`. */ int column_vector_multiplication(float r_vec[MAX_DIMENSIONS], VectorObject *vec, MatrixObject *mat) { @@ -1725,6 +2105,7 @@ static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2) return Vector_CreatePyObject_alloc(tvec, vec1->vec_num, Py_TYPE(vec1)); } +/** Multiplication (element-wise or scalar): `object * object`. */ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1776,7 +2157,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) return NULL; } -/* multiplication in-place: obj *= obj */ +/** Multiplication in-place (element-wise or scalar): `object *= object`. */ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1830,6 +2211,7 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) return v1; } +/** Multiplication (matrix multiply): `object @ object`. */ static PyObject *Vector_matmul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1893,6 +2275,7 @@ static PyObject *Vector_matmul(PyObject *v1, PyObject *v2) return NULL; } +/** Multiplication in-place (matrix multiply): `object @= object`. */ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) { PyErr_Format(PyExc_TypeError, @@ -1903,7 +2286,7 @@ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) return NULL; } -/* divide: obj / obj */ +/** Division: `object / object`. */ static PyObject *Vector_div(PyObject *v1, PyObject *v2) { float *vec = NULL, scalar; @@ -1950,7 +2333,7 @@ static PyObject *Vector_div(PyObject *v1, PyObject *v2) return Vector_CreatePyObject_alloc(vec, vec1->vec_num, Py_TYPE(v1)); } -/* divide in-place: obj /= obj */ +/** Division in-place: `object /= object`. */ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) { float scalar; @@ -1983,8 +2366,7 @@ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) return v1; } -/* -obj - * Returns the negative of this object. */ +/** Negative (returns the negative of this object): `-object`. */ static PyObject *Vector_neg(VectorObject *self) { float *tvec; @@ -1998,184 +2380,25 @@ static PyObject *Vector_neg(VectorObject *self) return Vector_CreatePyObject_alloc(tvec, self->vec_num, Py_TYPE(self)); } -/*------------------------tp_richcmpr - * returns -1 exception, 0 false, 1 true */ -static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type) -{ - VectorObject *vecA = NULL, *vecB = NULL; - int result = 0; - const double epsilon = 0.000001f; - double lenA, lenB; - - if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { - if (comparison_type == Py_NE) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; - } - vecA = (VectorObject *)objectA; - vecB = (VectorObject *)objectB; - - if (BaseMath_ReadCallback(vecA) == -1 || BaseMath_ReadCallback(vecB) == -1) { - return NULL; - } +/** \} */ - if (vecA->vec_num != vecB->vec_num) { - if (comparison_type == Py_NE) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; - } - - switch (comparison_type) { - case Py_LT: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA < lenB) { - result = 1; - } - break; - case Py_LE: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA < lenB) { - result = 1; - } - else { - result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); - } - break; - case Py_EQ: - result = EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); - break; - case Py_NE: - result = !EXPP_VectorsAreEqual(vecA->vec, vecB->vec, vecA->vec_num, 1); - break; - case Py_GT: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA > lenB) { - result = 1; - } - break; - case Py_GE: - lenA = len_squared_vn(vecA->vec, vecA->vec_num); - lenB = len_squared_vn(vecB->vec, vecB->vec_num); - if (lenA > lenB) { - result = 1; - } - else { - result = (((lenA + epsilon) > lenB) && ((lenA - epsilon) < lenB)); - } - break; - default: - printf("The result of the comparison could not be evaluated"); - break; - } - if (result == 1) { - Py_RETURN_TRUE; - } - - Py_RETURN_FALSE; -} - -static Py_hash_t Vector_hash(VectorObject *self) -{ - if (BaseMath_ReadCallback(self) == -1) { - return -1; - } +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Protocol Declarations + * \{ */ - if (BaseMathObject_Prepare_ForHash(self) == -1) { - return -1; - } - - return mathutils_array_hash(self->vec, self->vec_num); -} - -/*-----------------PROTCOL DECLARATIONS--------------------------*/ static PySequenceMethods Vector_SeqMethods = { - (lenfunc)Vector_len, /* sq_length */ - (binaryfunc)NULL, /* sq_concat */ - (ssizeargfunc)NULL, /* sq_repeat */ - (ssizeargfunc)Vector_item, /* sq_item */ - NULL, /* py3 deprecated slice func */ - (ssizeobjargproc)Vector_ass_item, /* sq_ass_item */ - NULL, /* py3 deprecated slice assign func */ - (objobjproc)NULL, /* sq_contains */ - (binaryfunc)NULL, /* sq_inplace_concat */ - (ssizeargfunc)NULL, /* sq_inplace_repeat */ + (lenfunc)Vector_len, /*sq_length*/ + (binaryfunc)NULL, /*sq_concat*/ + (ssizeargfunc)NULL, /*sq_repeat*/ + (ssizeargfunc)Vector_item, /*sq_item*/ + NULL, /*sq_slice(DEPRECATED)*/ + (ssizeobjargproc)Vector_ass_item, /*sq_ass_item*/ + NULL, /*sq_ass_slice(DEPRECATED)*/ + (objobjproc)NULL, /*sq_contains*/ + (binaryfunc)NULL, /*sq_inplace_concat */ + (ssizeargfunc)NULL, /*sq_inplace_repeat */ }; -static PyObject *Vector_subscript(VectorObject *self, PyObject *item) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i; - i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return NULL; - } - if (i < 0) { - i += self->vec_num; - } - return Vector_item(self, i); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { - return NULL; - } - - if (slicelength <= 0) { - return PyTuple_New(0); - } - if (step == 1) { - return Vector_slice(self, start, stop); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); - return NULL; - } - - PyErr_Format( - PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return NULL; -} - -static int Vector_ass_subscript(VectorObject *self, PyObject *item, PyObject *value) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) { - return -1; - } - if (i < 0) { - i += self->vec_num; - } - return Vector_ass_item(self, i, value); - } - if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->vec_num, &start, &stop, &step, &slicelength) < 0) { - return -1; - } - - if (step == 1) { - return Vector_ass_slice(self, start, stop, value); - } - - PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors"); - return -1; - } - - PyErr_Format( - PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name); - return -1; -} - static PyMappingMethods Vector_AsMapping = { (lenfunc)Vector_len, (binaryfunc)Vector_subscript, @@ -2202,28 +2425,32 @@ static PyNumberMethods Vector_NumMethods = { NULL, /*nb_int*/ NULL, /*nb_reserved*/ NULL, /*nb_float*/ - Vector_iadd, /* nb_inplace_add */ - Vector_isub, /* nb_inplace_subtract */ - Vector_imul, /* nb_inplace_multiply */ - NULL, /* nb_inplace_remainder */ - NULL, /* nb_inplace_power */ - NULL, /* nb_inplace_lshift */ - NULL, /* nb_inplace_rshift */ - NULL, /* nb_inplace_and */ - NULL, /* nb_inplace_xor */ - NULL, /* nb_inplace_or */ - NULL, /* nb_floor_divide */ - Vector_div, /* nb_true_divide */ - NULL, /* nb_inplace_floor_divide */ - Vector_idiv, /* nb_inplace_true_divide */ - NULL, /* nb_index */ - (binaryfunc)Vector_matmul, /* nb_matrix_multiply */ - (binaryfunc)Vector_imatmul, /* nb_inplace_matrix_multiply */ + Vector_iadd, /*nb_inplace_add*/ + Vector_isub, /*nb_inplace_subtract*/ + Vector_imul, /*nb_inplace_multiply*/ + NULL, /*nb_inplace_remainder*/ + NULL, /*nb_inplace_power*/ + NULL, /*nb_inplace_lshift*/ + NULL, /*nb_inplace_rshift*/ + NULL, /*nb_inplace_and*/ + NULL, /*nb_inplace_xor*/ + NULL, /*nb_inplace_or*/ + NULL, /*nb_floor_divide*/ + Vector_div, /*nb_true_divide*/ + NULL, /*nb_inplace_floor_divide*/ + Vector_idiv, /*nb_inplace_true_divide*/ + NULL, /*nb_index*/ + (binaryfunc)Vector_matmul, /*nb_matrix_multiply*/ + (binaryfunc)Vector_imatmul, /*nb_inplace_matrix_multiply*/ }; -/*------------------PY_OBECT DEFINITION--------------------------*/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Get/Set Item Implementation + * \{ */ -/* vector axis, vector.x/y/z/w */ +/* Vector axis: `vector.x/y/z/w`. */ PyDoc_STRVAR(Vector_axis_x_doc, "Vector X axis.\n\n:type: float"); PyDoc_STRVAR(Vector_axis_y_doc, "Vector Y axis.\n\n:type: float"); @@ -2240,7 +2467,7 @@ static int Vector_axis_set(VectorObject *self, PyObject *value, void *type) return vector_ass_item_internal(self, POINTER_AS_INT(type), value, true); } -/* vector.length */ +/* `Vector.length`. */ PyDoc_STRVAR(Vector_length_doc, "Vector Length.\n\n:type: float"); static PyObject *Vector_length_get(VectorObject *self, void *UNUSED(closure)) @@ -2296,7 +2523,7 @@ static int Vector_length_set(VectorObject *self, PyObject *value) return 0; } -/* vector.length_squared */ +/* `Vector.length_squared`. */ PyDoc_STRVAR(Vector_length_squared_doc, "Vector length squared (v.dot(v)).\n\n:type: float"); static PyObject *Vector_length_squared_get(VectorObject *self, void *UNUSED(closure)) { @@ -2503,9 +2730,12 @@ static int Vector_swizzle_set(VectorObject *self, PyObject *value, void *closure #define SWIZZLE3(a, b, c) POINTER_FROM_INT(_SWIZZLE3(a, b, c)) #define SWIZZLE4(a, b, c, d) POINTER_FROM_INT(_SWIZZLE4(a, b, c, d)) -/*****************************************************************************/ -/* Python attributes get/set structure: */ -/*****************************************************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Get/Set Item Definitions + * \{ */ + static PyGetSetDef Vector_getseters[] = { {"x", (getter)Vector_axis_get, (setter)Vector_axis_set, Vector_axis_x_doc, (void *)0}, {"y", (getter)Vector_axis_get, (setter)Vector_axis_set, Vector_axis_y_doc, (void *)1}, @@ -2886,68 +3116,11 @@ static PyGetSetDef Vector_getseters[] = { {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -/** - * Row vector multiplication - (Vector * Matrix) - * <pre> - * [x][y][z] * [1][4][7] - * [2][5][8] - * [3][6][9] - * </pre> - * \note vector/matrix multiplication is not commutative. - */ -static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], - VectorObject *vec, - MatrixObject *mat) -{ - float vec_cpy[MAX_DIMENSIONS]; - int row, col, z = 0, vec_num = vec->vec_num; - - if (mat->row_num != vec_num) { - if (mat->row_num == 4 && vec_num == 3) { - vec_cpy[3] = 1.0f; - } - else { - PyErr_SetString(PyExc_ValueError, - "vector * matrix: matrix column size " - "and the vector size must be the same"); - return -1; - } - } - - if (BaseMath_ReadCallback(vec) == -1 || BaseMath_ReadCallback(mat) == -1) { - return -1; - } - - memcpy(vec_cpy, vec->vec, vec_num * sizeof(float)); - - r_vec[3] = 1.0f; - /* Multiplication. */ - for (col = 0; col < mat->col_num; col++) { - double dot = 0.0; - for (row = 0; row < mat->row_num; row++) { - dot += (double)(MATRIX_ITEM(mat, row, col) * vec_cpy[row]); - } - r_vec[z++] = (float)dot; - } - return 0; -} - -/*----------------------------Vector.negate() -------------------- */ -PyDoc_STRVAR(Vector_negate_doc, - ".. method:: negate()\n" - "\n" - " Set all values to their negative.\n"); -static PyObject *Vector_negate(VectorObject *self) -{ - if (BaseMath_ReadCallback(self) == -1) { - return NULL; - } - - negate_vn(self->vec, self->vec_num); +/** \} */ - (void)BaseMath_WriteCallback(self); /* already checked for error */ - Py_RETURN_NONE; -} +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Method Definitions + * \{ */ static struct PyMethodDef Vector_methods[] = { /* Class Methods */ @@ -3000,11 +3173,15 @@ static struct PyMethodDef Vector_methods[] = { {NULL, NULL, 0, NULL}, }; -/** - * NOTE: #Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: Python Object Definition + * + * \note #Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing * but this means for eg that (vec * mat) and (mat * vec) * both get sent to Vector_mul and it needs to sort out the order - */ + * \{ */ PyDoc_STRVAR(vector_doc, ".. class:: Vector(seq)\n" @@ -3098,6 +3275,12 @@ PyTypeObject vector_Type = { NULL, }; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vector Type: C/API Constructors + * \{ */ + PyObject *Vector_CreatePyObject(const float *vec, const int vec_num, PyTypeObject *base_type) { VectorObject *self; @@ -3190,3 +3373,5 @@ PyObject *Vector_CreatePyObject_alloc(float *vec, const int vec_num, PyTypeObjec return (PyObject *)self; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Vector.h b/source/blender/python/mathutils/mathutils_Vector.h index 3bc4e9d6b6f..7006ece6bf0 100644 --- a/source/blender/python/mathutils/mathutils_Vector.h +++ b/source/blender/python/mathutils/mathutils_Vector.h @@ -18,7 +18,8 @@ typedef struct { int vec_num; } VectorObject; -/*prototypes*/ +/* Prototypes. */ + PyObject *Vector_CreatePyObject(const float *vec, int vec_num, PyTypeObject *base_type) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index bf876163013..9ffe2879779 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -96,7 +96,7 @@ typedef struct TriTessFace { const MVert *mverts[3]; const float *vert_normals[3]; const TSpace *tspace[3]; - float *loop_normal[3]; + const float *loop_normal[3]; float normal[3]; /* for flat faces */ bool is_smooth; } TriTessFace; @@ -451,9 +451,6 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval) { int i; - MVert *mvert; - TSpace *tspace = NULL; - float(*loop_normals)[3] = NULL; const int tottri = poly_to_tri_count(me->totpoly, me->totloop); MLoopTri *looptri; @@ -463,7 +460,7 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval unsigned int mpoly_prev = UINT_MAX; float no[3]; - mvert = CustomData_get_layer(&me->vdata, CD_MVERT); + const MVert *mvert = CustomData_get_layer(&me->vdata, CD_MVERT); looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); @@ -480,6 +477,8 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); } + const TSpace *tspace = NULL; + const float(*loop_normals)[3] = NULL; if (tangent) { BKE_mesh_ensure_normals_for_display(me_eval); BKE_mesh_calc_normals_split(me_eval); diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index f93397eedab..e3229e20595 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -10,6 +10,7 @@ #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -464,140 +465,141 @@ static void do_multires_bake(MultiresBakeRender *bkr, const MLoopTri *mlooptri = dm->getLoopTriArray(dm); const int lvl = bkr->lvl; int tot_tri = dm->getNumLoopTri(dm); + if (tot_tri < 1) { + return; + } - if (tot_tri > 0) { - MultiresBakeThread *handles; - MultiresBakeQueue queue; - - MVert *mvert = dm->getVertArray(dm); - MPoly *mpoly = dm->getPolyArray(dm); - MLoop *mloop = dm->getLoopArray(dm); - MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - float *pvtangent = NULL; - - ListBase threads; - int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); - - void *bake_data = NULL; - - Mesh *temp_mesh = BKE_mesh_new_nomain( - dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); - memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); - memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); - 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) { - BKE_mesh_calc_loop_tangent_ex( - dm->getVertArray(dm), - dm->getPolyArray(dm), - dm->getNumPolys(dm), - dm->getLoopArray(dm), - dm->getLoopTriArray(dm), - dm->getNumLoopTri(dm), - &dm->loopData, - true, - NULL, - 0, - vert_normals, - poly_normals, - (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), - (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); - } - - pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + MultiresBakeThread *handles; + MultiresBakeQueue queue; + + MVert *mvert = dm->getVertArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + MLoop *mloop = dm->getLoopArray(dm); + MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); + float *pvtangent = NULL; + + ListBase threads; + int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); + + void *bake_data = NULL; + + Mesh *temp_mesh = BKE_mesh_new_nomain( + dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm)); + memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert)); + memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge)); + 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) { + BKE_mesh_calc_loop_tangent_ex( + dm->getVertArray(dm), + dm->getPolyArray(dm), + dm->getNumPolys(dm), + dm->getLoopArray(dm), + dm->getLoopTriArray(dm), + dm->getNumLoopTri(dm), + &dm->loopData, + true, + NULL, + 0, + vert_normals, + poly_normals, + (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), + (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ + /* result */ + &dm->loopData, + dm->getNumLoops(dm), + &dm->tangent_mask); } - /* all threads shares the same custom bake data */ - if (initBakeData) { - bake_data = initBakeData(bkr, ibuf); - } + pvtangent = DM_get_loop_data_layer(dm, CD_TANGENT); + } - if (tot_thread > 1) { - BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); - } + /* all threads shares the same custom bake data */ + if (initBakeData) { + bake_data = initBakeData(bkr, ibuf); + } - handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); - - init_ccgdm_arrays(bkr->hires_dm); - - /* faces queue */ - queue.cur_tri = 0; - queue.tot_tri = tot_tri; - BLI_spin_init(&queue.spin); - - /* fill in threads handles */ - for (i = 0; i < tot_thread; i++) { - MultiresBakeThread *handle = &handles[i]; - - handle->bkr = bkr; - handle->image = ima; - handle->queue = &queue; - - handle->data.mpoly = mpoly; - handle->data.mvert = mvert; - handle->data.vert_normals = vert_normals; - handle->data.mloopuv = mloopuv; - BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); - handle->data.mlooptri = mlooptri; - handle->data.mloop = mloop; - handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ - handle->data.w = ibuf->x; - handle->data.h = ibuf->y; - handle->data.lores_dm = dm; - handle->data.hires_dm = bkr->hires_dm; - handle->data.lvl = lvl; - handle->data.pass_data = passKnownData; - handle->data.thread_data = handle; - handle->data.bake_data = bake_data; - handle->data.ibuf = ibuf; - - handle->height_min = FLT_MAX; - handle->height_max = -FLT_MAX; - - init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - - if (tot_thread > 1) { - BLI_threadpool_insert(&threads, handle); - } - } + if (tot_thread > 1) { + BLI_threadpool_init(&threads, do_multires_bake_thread, tot_thread); + } + + handles = MEM_callocN(tot_thread * sizeof(MultiresBakeThread), "do_multires_bake handles"); + + init_ccgdm_arrays(bkr->hires_dm); + + /* faces queue */ + queue.cur_tri = 0; + queue.tot_tri = tot_tri; + BLI_spin_init(&queue.spin); + + /* fill in threads handles */ + for (i = 0; i < tot_thread; i++) { + MultiresBakeThread *handle = &handles[i]; + + handle->bkr = bkr; + handle->image = ima; + handle->queue = &queue; + + handle->data.mpoly = mpoly; + handle->data.mvert = mvert; + handle->data.vert_normals = vert_normals; + handle->data.mloopuv = mloopuv; + BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); + handle->data.mlooptri = mlooptri; + handle->data.mloop = mloop; + handle->data.pvtangent = pvtangent; + handle->data.precomputed_normals = poly_normals; /* don't strictly need this */ + handle->data.w = ibuf->x; + handle->data.h = ibuf->y; + handle->data.lores_dm = dm; + handle->data.hires_dm = bkr->hires_dm; + handle->data.lvl = lvl; + handle->data.pass_data = passKnownData; + handle->data.thread_data = handle; + handle->data.bake_data = bake_data; + handle->data.ibuf = ibuf; + + handle->height_min = FLT_MAX; + handle->height_max = -FLT_MAX; + + init_bake_rast(&handle->bake_rast, ibuf, &handle->data, flush_pixel, bkr->do_update); - /* run threads */ if (tot_thread > 1) { - BLI_threadpool_end(&threads); + BLI_threadpool_insert(&threads, handle); } - else { - do_multires_bake_thread(&handles[0]); - } - - /* construct bake result */ - result->height_min = handles[0].height_min; - result->height_max = handles[0].height_max; + } - for (i = 1; i < tot_thread; i++) { - result->height_min = min_ff(result->height_min, handles[i].height_min); - result->height_max = max_ff(result->height_max, handles[i].height_max); - } + /* run threads */ + if (tot_thread > 1) { + BLI_threadpool_end(&threads); + } + else { + do_multires_bake_thread(&handles[0]); + } - BLI_spin_end(&queue.spin); + /* construct bake result */ + result->height_min = handles[0].height_min; + result->height_max = handles[0].height_max; - /* finalize baking */ - if (freeBakeData) { - freeBakeData(bake_data); - } + for (i = 1; i < tot_thread; i++) { + result->height_min = min_ff(result->height_min, handles[i].height_min); + result->height_max = max_ff(result->height_max, handles[i].height_max); + } - MEM_freeN(handles); + BLI_spin_end(&queue.spin); - BKE_id_free(NULL, temp_mesh); + /* finalize baking */ + if (freeBakeData) { + freeBakeData(bake_data); } + + MEM_freeN(handles); + + BKE_id_free(NULL, temp_mesh); } /* mode = 0: interpolate normals, diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 6c06cd663fa..43497fb8318 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -87,8 +87,6 @@ # include "FRS_freestyle.h" #endif -#include "DEG_depsgraph.h" - /* internal */ #include "pipeline.h" #include "render_result.h" diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 69c24a18f36..3b1eb293a3a 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -1215,7 +1215,7 @@ static int imagewraposa_aniso(Tex *tex, AFD.intpol = intpol; AFD.extflag = extflag; - /* brecht: added stupid clamping here, large dx/dy can give very large + /* NOTE(@brecht): added stupid clamping here, large dx/dy can give very large * filter sizes which take ages to render, it may be better to do this * more intelligently later in the code .. probably it's not noticeable */ if (AFD.dxt[0] * AFD.dxt[0] + AFD.dxt[1] * AFD.dxt[1] > 2.0f * 2.0f) { diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index 2d68148a86a..37ef9213615 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -12,6 +12,7 @@ #include "BLI_vector.hh" #include "BKE_DerivedMesh.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "DNA_mesh_types.h" @@ -492,11 +493,11 @@ static void generate_margin(ImBuf *ibuf, MPoly *mpoly; MLoop *mloop; - MLoopUV const *mloopuv; + const MLoopUV *mloopuv; int totpoly, totloop, totedge; int tottri; - MLoopTri const *looptri; + const MLoopTri *looptri; MLoopTri *looptri_mem = nullptr; if (me) { @@ -508,11 +509,11 @@ static void generate_margin(ImBuf *ibuf, mloop = me->mloop; if ((uv_layer == nullptr) || (uv_layer[0] == '\0')) { - mloopuv = static_cast<MLoopUV const *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); + mloopuv = static_cast<const MLoopUV *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV)); } else { int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer); - mloopuv = static_cast<MLoopUV const *>( + mloopuv = static_cast<const MLoopUV *>( CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uv_id)); } diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 8ba3bac7cad..6e0bae700dc 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -271,7 +271,6 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, { const MLoop *mloop = mesh->mloop; const int totloop = mesh->totloop; - const MLoopCol *mcol; char layername[MAX_CUSTOMDATA_LAYER_NAME]; int i; @@ -282,7 +281,7 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, } CustomData_validate_layer_name( &mesh->ldata, CD_PROP_BYTE_COLOR, pd->vertex_attribute_name, layername); - mcol = CustomData_get_layer_named(&mesh->ldata, CD_PROP_BYTE_COLOR, layername); + const MLoopCol *mcol = CustomData_get_layer_named(&mesh->ldata, CD_PROP_BYTE_COLOR, layername); if (!mcol) { return; } @@ -323,13 +322,12 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, float *data_color) { const int totvert = mesh->totvert; - const MDeformVert *mdef, *dv; int mdef_index; int i; BLI_assert(data_color); - mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); + const MDeformVert *mdef = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); if (!mdef) { return; } @@ -341,6 +339,7 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, return; } + const MDeformVert *dv; for (i = 0, dv = mdef; i < totvert; i++, dv++, data_color += 3) { MDeformWeight *dw; int j; diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index 73dbcfdfeab..ce58993b7cf 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -1208,9 +1208,8 @@ static int multitex(Tex *tex, case TEX_MUSGRAVE: /* newnoise: musgrave types */ - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); @@ -1230,18 +1229,16 @@ static int multitex(Tex *tex, break; /* newnoise: voronoi type */ case TEX_VORONOI: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); retval = voronoiTex(tex, tmpvec, texres); break; case TEX_DISTNOISE: - /* ton: added this, for Blender convention reason. - * artificer: added the use of tmpvec to avoid scaling texvec - */ + /* NOTE(@ton): added this, for Blender convention reason. + * NOTE(@artificer): added the use of tmpvec to avoid scaling texvec. */ copy_v3_v3(tmpvec, texvec); mul_v3_fl(tmpvec, 1.0f / tex->noisesize); diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index b4adc950d6b..1da6efb1d04 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -60,6 +60,13 @@ void SEQ_time_update_recursive(struct Scene *scene, struct Sequence *changed_seq */ bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, int timeline_frame); void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta); +bool SEQ_time_has_still_frames(const struct Sequence *seq); +bool SEQ_time_has_left_still_frames(const struct Sequence *seq); +bool SEQ_time_has_right_still_frames(const struct Sequence *seq); +int SEQ_time_left_handle_frame_get(struct Sequence *seq); +int SEQ_time_right_handle_frame_get(struct Sequence *seq); +void SEQ_time_left_handle_frame_set(struct Sequence *seq, int val); +void SEQ_time_right_handle_frame_set(struct Sequence *seq, int val); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index ea90a3ed372..bd4258bfdb1 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -17,10 +17,6 @@ struct Scene; struct SeqCollection; struct Sequence; -int SEQ_transform_get_left_handle_frame(struct Sequence *seq); -int SEQ_transform_get_right_handle_frame(struct Sequence *seq); -void SEQ_transform_set_left_handle_frame(struct Sequence *seq, int val); -void SEQ_transform_set_right_handle_frame(struct Sequence *seq, int val); /** * Use to impose limits when dragging/extending - so impossible situations don't happen. * Can't use the #SEQ_LEFTSEL and #SEQ_LEFTSEL directly because the strip may be in a meta-strip. diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c index 0fdaef61b65..f8e8fc32a5d 100644 --- a/source/blender/sequencer/intern/disk_cache.c +++ b/source/blender/sequencer/intern/disk_cache.c @@ -344,15 +344,15 @@ static void seq_disk_cache_create_version_file(char *path) static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) { - char path[FILE_MAX]; + char filepath[FILE_MAX]; char path_version_file[FILE_MAX]; int version = 0; - seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); - BLI_strncpy(path_version_file, path, sizeof(path_version_file)); + seq_disk_cache_get_project_dir(disk_cache, filepath, sizeof(filepath)); + BLI_strncpy(path_version_file, filepath, sizeof(path_version_file)); BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); - if (BLI_exists(path) && BLI_is_dir(path)) { + if (BLI_exists(filepath) && BLI_is_dir(filepath)) { FILE *file = BLI_fopen(path_version_file, "r"); if (file) { @@ -364,7 +364,7 @@ static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) } if (version != DCACHE_CURRENT_VERSION) { - BLI_delete(path, false, true); + BLI_delete(filepath, false, true); seq_disk_cache_create_version_file(path_version_file); } } @@ -548,22 +548,22 @@ bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf { BLI_mutex_lock(&disk_cache->read_write_mutex); - char path[FILE_MAX]; + char filepath[FILE_MAX]; - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); + seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); + BLI_make_existing_file(filepath); - FILE *file = BLI_fopen(path, "rb+"); + FILE *file = BLI_fopen(filepath, "rb+"); if (!file) { - file = BLI_fopen(path, "wb+"); + file = BLI_fopen(filepath, "wb+"); if (!file) { BLI_mutex_unlock(&disk_cache->read_write_mutex); return false; } - seq_disk_cache_add_file_to_list(disk_cache, path); + seq_disk_cache_add_file_to_list(disk_cache, filepath); } - DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, filepath); DiskCacheHeader header; memset(&header, 0, sizeof(header)); /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading @@ -585,7 +585,7 @@ bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf */ header.entry[entry_index].size_compressed = bytes_written; seq_disk_cache_write_header(file, &header); - seq_disk_cache_update_file(disk_cache, path); + seq_disk_cache_update_file(disk_cache, filepath); fclose(file); BLI_mutex_unlock(&disk_cache->read_write_mutex); @@ -600,13 +600,13 @@ ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) { BLI_mutex_lock(&disk_cache->read_write_mutex); - char path[FILE_MAX]; + char filepath[FILE_MAX]; DiskCacheHeader header; - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); + seq_disk_cache_get_file_path(disk_cache, key, filepath, sizeof(filepath)); + BLI_make_existing_file(filepath); - FILE *file = BLI_fopen(path, "rb"); + FILE *file = BLI_fopen(filepath, "rb"); if (!file) { BLI_mutex_unlock(&disk_cache->read_write_mutex); return NULL; @@ -656,8 +656,8 @@ ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) BLI_mutex_unlock(&disk_cache->read_write_mutex); return NULL; } - BLI_file_touch(path); - seq_disk_cache_update_file(disk_cache, path); + BLI_file_touch(filepath); + seq_disk_cache_update_file(disk_cache, filepath); fclose(file); BLI_mutex_unlock(&disk_cache->read_write_mutex); diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 91b69bfe01f..59b4c6de1ef 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -523,9 +523,7 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, SeqRenderState state; seq_render_state_init(&state); - for (timeline_frame = seq->startdisp + seq->startstill; - timeline_frame < seq->enddisp - seq->endstill; - timeline_frame++) { + for (timeline_frame = seq->startdisp; timeline_frame < seq->enddisp; timeline_frame++) { if (context->size_flags & IMB_PROXY_25) { seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 25, overwrite); } @@ -539,8 +537,7 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, seq_proxy_build_frame(&render_context, &state, seq, timeline_frame, 100, overwrite); } - *progress = (float)(timeline_frame - seq->startdisp - seq->startstill) / - (seq->enddisp - seq->endstill - seq->startdisp - seq->startstill); + *progress = (float)(timeline_frame - seq->startdisp) / (seq->enddisp - seq->startdisp); *do_update = true; if (*stop || G.is_break) { diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 1b9e89a35d5..b0898be3765 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -2088,7 +2088,8 @@ void SEQ_render_thumbnails(const SeqRenderData *context, /* Adding the hold offset value (seq->anim_startofs) to the start frame. Position of image not * affected, but frame loaded affected. */ - float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + float upper_thumb_bound = SEQ_time_has_right_still_frames(seq) ? (seq->start + seq->len) : + seq->enddisp; upper_thumb_bound = (upper_thumb_bound > view_area->xmax) ? view_area->xmax + frame_step : upper_thumb_bound; @@ -2121,7 +2122,9 @@ void SEQ_render_thumbnails(const SeqRenderData *context, int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq) { - const int content_len = (seq->enddisp - seq->startdisp - seq->startstill - seq->endstill); + const int content_start = max_ii(seq->startdisp, seq->start); + const int content_end = min_ii(seq->enddisp, seq->start + seq->len); + const int content_len = content_end - content_start; /* Arbitrary, but due to performance reasons should be as low as possible. */ const int thumbnails_base_set_count = min_ii(content_len / 100, 30); diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index faa527786fd..ad57412034a 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -129,7 +129,13 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int seq->pitch = 1.0f; seq->scene_sound = NULL; seq->type = type; - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + + if (seq->type == SEQ_TYPE_ADJUSTMENT) { + seq->blend_mode = SEQ_TYPE_CROSS; + } + else { + seq->blend_mode = SEQ_TYPE_ALPHAOVER; + } seq->strip = seq_strip_alloc(type); seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index f8e26a56692..77b0fc946d9 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -174,7 +174,7 @@ Sequence *SEQ_add_effect_strip(Scene *scene, ListBase *seqbase, struct SeqLoadDa if (!load_data->effect.seq1) { seq->len = 1; /* Effect is generator, set non zero length. */ - SEQ_transform_set_right_handle_frame(seq, load_data->effect.end_frame); + SEQ_time_right_handle_frame_set(seq, load_data->effect.end_frame); } seq_add_set_name(scene, seq, load_data); @@ -656,8 +656,8 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo free_proxy_seq(seq); if (lock_range) { - SEQ_transform_set_left_handle_frame(seq, prev_startdisp); - SEQ_transform_set_right_handle_frame(seq, prev_enddisp); + SEQ_time_left_handle_frame_set(seq, prev_startdisp); + SEQ_time_right_handle_frame_set(seq, prev_enddisp); SEQ_transform_fix_single_image_seq_offsets(seq); } diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 6c7bb71cb75..96bfce8f740 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -83,8 +83,6 @@ int SEQ_edit_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_ SWAP(int, seq_a->start, seq_b->start); SWAP(int, seq_a->startofs, seq_b->startofs); SWAP(int, seq_a->endofs, seq_b->endofs); - SWAP(int, seq_a->startstill, seq_b->startstill); - SWAP(int, seq_a->endstill, seq_b->endstill); SWAP(int, seq_a->machine, seq_b->machine); SWAP(int, seq_a->startdisp, seq_b->startdisp); SWAP(int, seq_a->enddisp, seq_b->enddisp); @@ -278,20 +276,22 @@ static void seq_split_set_left_hold_offset(Sequence *seq, int timeline_frame) { /* Adjust within range of extended stillframes before strip. */ if (timeline_frame < seq->start) { - seq->start = timeline_frame - 1; - seq->anim_endofs += seq->len - 1; - seq->startstill = timeline_frame - seq->startdisp - 1; - seq->endstill = 0; + SEQ_time_left_handle_frame_set(seq, timeline_frame); } /* Adjust within range of strip contents. */ else if ((timeline_frame >= seq->start) && (timeline_frame <= (seq->start + seq->len))) { - seq->endofs = 0; - seq->endstill = 0; - seq->anim_endofs += (seq->start + seq->len) - timeline_frame; + seq->anim_startofs += timeline_frame - seq->start; + seq->start = timeline_frame; + seq->startofs = 0; } /* Adjust within range of extended stillframes after strip. */ else if ((seq->start + seq->len) < timeline_frame) { - seq->endstill = timeline_frame - seq->start - seq->len; + const int right_handle_backup = SEQ_time_right_handle_frame_get(seq); + seq->start += timeline_frame - seq->start; + seq->anim_startofs += seq->len - 1; + seq->len = 1; + SEQ_time_left_handle_frame_set(seq, timeline_frame); + SEQ_time_right_handle_frame_set(seq, right_handle_backup); } } @@ -299,22 +299,19 @@ static void seq_split_set_right_hold_offset(Sequence *seq, int timeline_frame) { /* Adjust within range of extended stillframes before strip. */ if (timeline_frame < seq->start) { - seq->startstill = seq->start - timeline_frame; + const int left_handle_backup = SEQ_time_left_handle_frame_get(seq); + seq->start = timeline_frame - 1; + SEQ_time_left_handle_frame_set(seq, left_handle_backup); + SEQ_time_right_handle_frame_set(seq, timeline_frame); } /* Adjust within range of strip contents. */ else if ((timeline_frame >= seq->start) && (timeline_frame <= (seq->start + seq->len))) { - seq->anim_startofs += timeline_frame - seq->start; - seq->start = timeline_frame; - seq->startstill = 0; - seq->startofs = 0; + seq->anim_endofs += seq->start + seq->len - timeline_frame; + seq->endofs = 0; } /* Adjust within range of extended stillframes after strip. */ else if ((seq->start + seq->len) < timeline_frame) { - seq->start = timeline_frame; - seq->startofs = 0; - seq->anim_startofs += seq->len - 1; - seq->endstill = seq->enddisp - timeline_frame - 1; - seq->startstill = 0; + SEQ_time_right_handle_frame_set(seq, timeline_frame); } } @@ -322,29 +319,24 @@ static void seq_split_set_right_offset(Sequence *seq, int timeline_frame) { /* Adjust within range of extended stillframes before strip. */ if (timeline_frame < seq->start) { + const int content_offset = seq->start - timeline_frame + 1; seq->start = timeline_frame - 1; - seq->startstill = timeline_frame - seq->startdisp - 1; - seq->endofs = seq->len - 1; - } - /* Adjust within range of extended stillframes after strip. */ - else if ((seq->start + seq->len) < timeline_frame) { - seq->endstill -= seq->enddisp - timeline_frame; + seq->startofs += content_offset; } - SEQ_transform_set_right_handle_frame(seq, timeline_frame); + + SEQ_time_right_handle_frame_set(seq, timeline_frame); } static void seq_split_set_left_offset(Sequence *seq, int timeline_frame) { - /* Adjust within range of extended stillframes before strip. */ - if (timeline_frame < seq->start) { - seq->startstill = seq->start - timeline_frame; - } /* Adjust within range of extended stillframes after strip. */ - if ((seq->start + seq->len) < timeline_frame) { - seq->start = timeline_frame - seq->len + 1; - seq->endstill = seq->enddisp - timeline_frame - 1; + if (timeline_frame > seq->start + seq->len) { + const int content_offset = timeline_frame - (seq->start + seq->len) + 1; + seq->start += content_offset; + seq->endofs += content_offset; } - SEQ_transform_set_left_handle_frame(seq, timeline_frame); + + SEQ_time_left_handle_frame_set(seq, timeline_frame); } static bool seq_edit_split_effect_intersect_check(const Sequence *seq, const int timeline_frame) diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index a5341dbc528..e4f7a5e87e8 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -139,15 +139,8 @@ void seq_update_sound_bounds_recursive(Scene *scene, Sequence *metaseq) static void seq_time_update_sequence_bounds(Scene *scene, Sequence *seq) { - if (seq->startofs && seq->startstill) { - seq->startstill = 0; - } - if (seq->endofs && seq->endstill) { - seq->endstill = 0; - } - - seq->startdisp = seq->start + seq->startofs - seq->startstill; - seq->enddisp = seq->start + seq->len - seq->endofs + seq->endstill; + seq->startdisp = seq->start + seq->startofs; + seq->enddisp = seq->start + seq->len - seq->endofs; if (seq->type == SEQ_TYPE_META) { seq_update_sound_bounds_recursive(scene, seq); @@ -184,8 +177,8 @@ void SEQ_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) seq_time_update_meta_strip(scene, seq_meta); /* Prevent meta-strip to move in timeline. */ - SEQ_transform_set_left_handle_frame(seq_meta, seq_meta->startdisp); - SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp); + SEQ_time_left_handle_frame_set(seq_meta, seq_meta->startdisp); + SEQ_time_right_handle_frame_set(seq_meta, seq_meta->enddisp); } void SEQ_time_update_sequence(Scene *scene, ListBase *seqbase, Sequence *seq) @@ -204,7 +197,7 @@ void SEQ_time_update_sequence(Scene *scene, ListBase *seqbase, Sequence *seq) /* effects and meta: automatic start and end */ if (seq->type & SEQ_TYPE_EFFECT) { if (seq->seq1) { - seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0; + seq->startofs = seq->endofs = 0; if (seq->seq3) { seq->start = seq->startdisp = max_iii( seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp); @@ -516,3 +509,37 @@ bool SEQ_time_strip_intersects_frame(const Sequence *seq, const int timeline_fra { return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame); } + +bool SEQ_time_has_left_still_frames(const Sequence *seq) +{ + return seq->startofs < 0; +} + +bool SEQ_time_has_right_still_frames(const Sequence *seq) +{ + return seq->endofs < 0; +} + +bool SEQ_time_has_still_frames(const Sequence *seq) +{ + return SEQ_time_has_right_still_frames(seq) || SEQ_time_has_left_still_frames(seq); +} + +int SEQ_time_left_handle_frame_get(Sequence *seq) +{ + return seq->start + seq->startofs; +} +int SEQ_time_right_handle_frame_get(Sequence *seq) +{ + return seq->start + seq->len - seq->endofs; +} + +void SEQ_time_left_handle_frame_set(Sequence *seq, int val) +{ + seq->startofs = val - seq->start; +} + +void SEQ_time_right_handle_frame_set(Sequence *seq, int val) +{ + seq->endofs = seq->start + seq->len - val; +} diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 2c9ab0a3335..8ff577240d6 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -38,39 +38,6 @@ static int seq_tx_get_end(Sequence *seq) return seq->start + seq->len; } -int SEQ_transform_get_left_handle_frame(Sequence *seq) -{ - return (seq->start - seq->startstill) + seq->startofs; -} -int SEQ_transform_get_right_handle_frame(Sequence *seq) -{ - return ((seq->start + seq->len) + seq->endstill) - seq->endofs; -} - -void SEQ_transform_set_left_handle_frame(Sequence *seq, int val) -{ - if (val < (seq)->start) { - seq->startstill = abs(val - (seq)->start); - seq->startofs = 0; - } - else { - seq->startofs = abs(val - (seq)->start); - seq->startstill = 0; - } -} - -void SEQ_transform_set_right_handle_frame(Sequence *seq, int val) -{ - if (val > (seq)->start + (seq)->len) { - seq->endstill = abs(val - (seq->start + (seq)->len)); - seq->endofs = 0; - } - else { - seq->endofs = abs(val - ((seq)->start + (seq)->len)); - seq->endstill = 0; - } -} - bool SEQ_transform_single_image_check(Sequence *seq) { return ((seq->len == 1) && @@ -122,13 +89,13 @@ bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase) void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag) { if (leftflag) { - if (SEQ_transform_get_left_handle_frame(seq) >= SEQ_transform_get_right_handle_frame(seq)) { - SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - 1); + if (SEQ_time_left_handle_frame_get(seq) >= SEQ_time_right_handle_frame_get(seq)) { + SEQ_time_left_handle_frame_set(seq, SEQ_time_right_handle_frame_get(seq) - 1); } if (SEQ_transform_single_image_check(seq) == 0) { - if (SEQ_transform_get_left_handle_frame(seq) >= seq_tx_get_end(seq)) { - SEQ_transform_set_left_handle_frame(seq, seq_tx_get_end(seq) - 1); + if (SEQ_time_left_handle_frame_get(seq) >= seq_tx_get_end(seq)) { + SEQ_time_left_handle_frame_set(seq, seq_tx_get_end(seq) - 1); } /* TODO: This doesn't work at the moment. */ @@ -144,21 +111,21 @@ void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag) } if (rightflag) { - if (SEQ_transform_get_right_handle_frame(seq) <= SEQ_transform_get_left_handle_frame(seq)) { - SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + 1); + if (SEQ_time_right_handle_frame_get(seq) <= SEQ_time_left_handle_frame_get(seq)) { + SEQ_time_right_handle_frame_set(seq, SEQ_time_left_handle_frame_get(seq) + 1); } if (SEQ_transform_single_image_check(seq) == 0) { - if (SEQ_transform_get_right_handle_frame(seq) <= seq_tx_get_start(seq)) { - SEQ_transform_set_right_handle_frame(seq, seq_tx_get_start(seq) + 1); + if (SEQ_time_right_handle_frame_get(seq) <= seq_tx_get_start(seq)) { + SEQ_time_right_handle_frame_set(seq, seq_tx_get_start(seq) + 1); } } } /* sounds cannot be extended past their endpoints */ if (seq->type == SEQ_TYPE_SOUND_RAM) { - seq->startstill = 0; - seq->endstill = 0; + CLAMP(seq->startofs, 0, MAXFRAME); + CLAMP(seq->endofs, 0, MAXFRAME); } } @@ -171,12 +138,12 @@ void SEQ_transform_fix_single_image_seq_offsets(Sequence *seq) /* make sure the image is always at the start since there is only one, * adjusting its start should be ok */ - left = SEQ_transform_get_left_handle_frame(seq); + left = SEQ_time_left_handle_frame_get(seq); start = seq->start; if (start != left) { offset = left - start; - SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) - offset); - SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - offset); + SEQ_time_left_handle_frame_set(seq, SEQ_time_left_handle_frame_get(seq) - offset); + SEQ_time_right_handle_frame_set(seq, SEQ_time_right_handle_frame_get(seq) - offset); seq->start += offset; } } @@ -227,8 +194,8 @@ void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delt * start/end point in timeline. */ SEQ_time_update_meta_strip_range(evil_scene, seq); /* Move meta start/end points. */ - SEQ_transform_set_left_handle_frame(seq, seq->startdisp + delta); - SEQ_transform_set_right_handle_frame(seq, seq->enddisp + delta); + SEQ_time_left_handle_frame_set(seq, seq->startdisp + delta); + SEQ_time_right_handle_frame_set(seq, seq->enddisp + delta); } ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(evil_scene)); diff --git a/source/blender/simulation/intern/SIM_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp index 46597f80d75..f5a6427a6a4 100644 --- a/source/blender/simulation/intern/SIM_mass_spring.cpp +++ b/source/blender/simulation/intern/SIM_mass_spring.cpp @@ -268,9 +268,8 @@ static void cloth_setup_constraints(ClothModifierData *clmd) /** * Computes where the cloth would be if it were subject to perfectly stiff edges - * (edge distance constraints) in a lagrangian solver. then add forces to help - * guide the implicit solver to that state. this function is called after - * collisions. + * (edge distance constraints) in a lagrangian solver. Then add forces to help + * guide the implicit solver to that state. This function is called after collisions. */ static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index b87fa896e69..890872a06bc 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -781,7 +781,7 @@ void WM_operator_properties_filesel(struct wmOperatorType *ot, */ void WM_operator_properties_id_lookup_set_from_id(PointerRNA *ptr, const ID *id); /** - * Tries to find an ID in \a bmain. There needs to be either a "name" string or "session_uuid" int + * Tries to find an ID in \a bmain. There needs to be either a "session_uuid" int or "name" string * property defined and set. The former has priority. See #WM_operator_properties_id_lookup() for a * helper to add the properties. */ @@ -789,6 +789,11 @@ struct ID *WM_operator_properties_id_lookup_from_name_or_session_uuid(struct Mai PointerRNA *ptr, enum ID_Type type); /** + * Check if either the "session_uuid" or "name" property is set inside \a ptr. If this is the case + * the ID can be looked up by #WM_operator_properties_id_lookup_from_name_or_session_uuid(). + */ +bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr); +/** * Adds "name" and "session_uuid" properties so the caller can tell the operator which ID to act * on. See #WM_operator_properties_id_lookup_from_name_or_session_uuid(). Both properties will be * hidden in the UI and not be saved over consecutive operator calls. @@ -925,12 +930,14 @@ char *WM_prop_pystring_assign(struct bContext *C, int index); /** * Convert: `some.op` -> `SOME_OT_op` or leave as-is. + * \return the length of `dst`. */ -void WM_operator_bl_idname(char *to, const char *from); +size_t WM_operator_bl_idname(char *dst, const char *src) ATTR_NONNULL(1, 2); /** * Convert: `SOME_OT_op` -> `some.op` or leave as-is. + * \return the length of `dst`. */ -void WM_operator_py_idname(char *to, const char *from); +size_t WM_operator_py_idname(char *dst, const char *src) ATTR_NONNULL(1, 2); /** * Sanity check to ensure #WM_operator_bl_idname won't fail. * \returns true when there are no problems with \a idname, otherwise report an error. @@ -967,6 +974,14 @@ bool WM_operatortype_remove(const char *idname); * Remove memory of all previously executed tools. */ void WM_operatortype_last_properties_clear_all(void); + +void WM_operatortype_idname_visit_for_search(const struct bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /** * Tag all operator-properties of \a ot defined after calling this, until * the next #WM_operatortype_props_advanced_end call (if available), with @@ -1070,6 +1085,13 @@ void WM_menutype_freelink(struct MenuType *mt); void WM_menutype_free(void); bool WM_menutype_poll(struct bContext *C, struct MenuType *mt); +void WM_menutype_idname_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /* wm_panel_type.c */ /** @@ -1081,6 +1103,13 @@ struct PanelType *WM_paneltype_find(const char *idname, bool quiet); bool WM_paneltype_add(struct PanelType *pt); void WM_paneltype_remove(struct PanelType *pt); +void WM_paneltype_idname_visit_for_search(const struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + const char *edit_text, + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data); + /* wm_gesture_ops.c */ int WM_gesture_box_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); @@ -1226,7 +1255,7 @@ struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short id bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); /** - * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + * \note Does not store \a asset in any way, so it's fine to pass a temporary. */ wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, struct AssetMetaData *metadata, @@ -1258,7 +1287,7 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag); /** - * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + * \note Does not store \a asset in any way, so it's fine to pass a temporary. */ void WM_drag_add_asset_list_item(wmDrag *drag, const struct bContext *C, @@ -1671,7 +1700,7 @@ void WM_xr_action_binding_destroy(wmXrData *xr, /** * If action_set_name is NULL, then all action sets will be treated as active. */ -bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool delayed); bool WM_xr_controller_pose_actions_set(wmXrData *xr, const char *action_set_name, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index a9b8d91ca03..9e9f195c430 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -812,6 +812,10 @@ typedef struct wmXrActionData { char action_set[64]; /** Action name. */ char action[64]; + /** User path. E.g. "/user/hand/left" */ + char user_path[64]; + /** Other user path, for bimanual actions. E.g. "/user/hand/right" */ + char user_path_other[64]; /** Type. */ eXrActionType type; /** State. Set appropriately based on type. */ @@ -1193,8 +1197,9 @@ typedef struct wmDropBox { struct wmDrag *drag, const int xy[2]); - /** Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. - * \note Only setups the drawing buffer for drawing in view, not the GPU transform matricies. + /** + * Called with the draw buffer (#GPUViewport) set up for drawing into the region's view. + * \note Only setups the drawing buffer for drawing in view, not the GPU transform matrices. * The callback has to do that itself, with for example #UI_view2d_view_ortho. * \param xy: Cursor location in window coordinates (#wmEvent.xy compatible). */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index c806472103d..3ff3117dafe 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4019,6 +4019,7 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval event.type = EVT_FILESELECT; event.val = eventval; + event.flag = 0; event.customdata = ophandle; /* Only as void pointer type check. */ wm_event_add(win, &event); @@ -5845,6 +5846,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wmEvent test_event = *win->eventstate; test_event.type = event_data[data_index].event_type; test_event.val = event_data[data_index].event_value; + test_event.flag = 0; wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { @@ -5911,7 +5913,7 @@ bool WM_window_modal_keymap_status_draw(bContext *C, wmWindow *win, uiLayout *la bool show_text = true; { - /* Warning: O(n^2). */ + /* WARNING: O(n^2). */ wmKeyMapItem *kmi = NULL; for (kmi = keymap->items.first; kmi; kmi = kmi->next) { if (kmi->propvalue == items[i].value) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index bee7e71df54..9578f82d3d4 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -846,8 +846,13 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) bf_reports->count.missing_obproxies); } else { - BLI_assert(bf_reports->count.missing_obdata == 0); - BLI_assert(bf_reports->count.missing_obproxies == 0); + if (bf_reports->count.missing_obdata != 0 || bf_reports->count.missing_obproxies != 0) { + CLOG_ERROR(&LOG, + "%d local ObjectData and %d local Object proxies are reported to be missing, " + "this should never happen", + bf_reports->count.missing_obdata, + bf_reports->count.missing_obproxies); + } } if (bf_reports->resynced_lib_overrides_libraries_count != 0) { diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 1fdc8bbe2c8..e3892933905 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -819,6 +819,7 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev /* Toggle flipping on/off. */ gesture->use_flip = !gesture->use_flip; gesture_straightline_apply(C, op); + wm_gesture_tag_redraw(win); break; } case GESTURE_MODAL_SELECT: { @@ -897,6 +898,7 @@ int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmE case GESTURE_MODAL_FLIP: { /* Toggle flip on/off. */ gesture->use_flip = !gesture->use_flip; + wm_gesture_tag_redraw(win); break; } case GESTURE_MODAL_SELECT: diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 8d6741dcfb6..dbce360cb61 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -59,6 +59,7 @@ #include "BKE_mask.h" /* free mask clipboard */ #include "BKE_material.h" /* BKE_material_copybuf_clear */ #include "BKE_studiolight.h" +#include "BKE_subdiv.h" #include "BKE_tracking.h" /* free tracking clipboard */ #include "RE_engine.h" @@ -114,9 +115,6 @@ #include "GPU_init_exit.h" #include "GPU_material.h" -#include "BKE_sound.h" -#include "BKE_subdiv.h" - #include "COM_compositor.h" #include "DEG_depsgraph.h" diff --git a/source/blender/windowmanager/intern/wm_menu_type.c b/source/blender/windowmanager/intern/wm_menu_type.c index 9e50ebb1ce5..b4cf5a79cfa 100644 --- a/source/blender/windowmanager/intern/wm_menu_type.c +++ b/source/blender/windowmanager/intern/wm_menu_type.c @@ -99,3 +99,21 @@ bool WM_menutype_poll(bContext *C, MenuType *mt) } return true; } + +void WM_menutype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, menutypes_hash) { + MenuType *mt = BLI_ghashIterator_getValue(&gh_iter); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = mt->idname; + visit_params.info = mt->label; + visit_fn(visit_user_data, &visit_params); + } +} diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 3476a16b02a..71c948dfbb9 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -246,23 +246,28 @@ ID *WM_operator_properties_id_lookup_from_name_or_session_uuid(Main *bmain, PointerRNA *ptr, const ID_Type type) { - PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name"); PropertyRNA *prop_session_uuid = RNA_struct_find_property(ptr, "session_uuid"); + if (prop_session_uuid && RNA_property_is_set(ptr, prop_session_uuid)) { + const uint32_t session_uuid = (uint32_t)RNA_property_int_get(ptr, prop_session_uuid); + return BKE_libblock_find_session_uuid(bmain, type, session_uuid); + } + PropertyRNA *prop_name = RNA_struct_find_property(ptr, "name"); if (prop_name && RNA_property_is_set(ptr, prop_name)) { char name[MAX_ID_NAME - 2]; RNA_property_string_get(ptr, prop_name, name); return BKE_libblock_find_name(bmain, type, name); } - if (prop_session_uuid && RNA_property_is_set(ptr, prop_session_uuid)) { - const uint32_t session_uuid = (uint32_t)RNA_property_int_get(ptr, prop_session_uuid); - return BKE_libblock_find_session_uuid(bmain, type, session_uuid); - } - return NULL; } +bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr) +{ + return RNA_struct_property_is_set(ptr, "session_uuid") || + RNA_struct_property_is_set(ptr, "name"); +} + void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop) { PropertyRNA *prop; diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 8aa8469f0bc..d1c27504628 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -246,6 +246,27 @@ void WM_operatortype_last_properties_clear_all(void) } } +void WM_operatortype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, global_ops_hash) { + wmOperatorType *ot = BLI_ghashIterator_getValue(&gh_iter); + + char idname_py[OP_MAX_TYPENAME]; + WM_operator_py_idname(idname_py, ot->idname); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = idname_py; + visit_params.info = ot->name; + visit_fn(visit_user_data, &visit_params); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b8107a49a4c..307d3282659 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -106,47 +106,38 @@ /** \name Operator API * \{ */ -void WM_operator_py_idname(char *to, const char *from) +size_t WM_operator_py_idname(char *dst, const char *src) { - const char *sep = strstr(from, "_OT_"); + const char *sep = strstr(src, "_OT_"); if (sep) { - int ofs = (sep - from); + int ofs = (sep - src); /* NOTE: we use ascii `tolower` instead of system `tolower`, because the * latter depends on the locale, and can lead to `idname` mismatch. */ - memcpy(to, from, sizeof(char) * ofs); - BLI_str_tolower_ascii(to, ofs); + memcpy(dst, src, sizeof(char) * ofs); + BLI_str_tolower_ascii(dst, ofs); - to[ofs] = '.'; - BLI_strncpy(to + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1)); - } - else { - /* should not happen but support just in case */ - BLI_strncpy(to, from, OP_MAX_TYPENAME); + dst[ofs] = '.'; + return BLI_strncpy_rlen(dst + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1)) + (ofs + 1); } + /* Should not happen but support just in case. */ + return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME); } -void WM_operator_bl_idname(char *to, const char *from) +size_t WM_operator_bl_idname(char *dst, const char *src) { - if (from) { - const char *sep = strchr(from, '.'); - - int from_len; - if (sep && (from_len = strlen(from)) < OP_MAX_TYPENAME - 3) { - const int ofs = (sep - from); - memcpy(to, from, sizeof(char) * ofs); - BLI_str_toupper_ascii(to, ofs); - memcpy(to + ofs, "_OT_", 4); - memcpy(to + (ofs + 4), sep + 1, (from_len - ofs)); - } - else { - /* should not happen but support just in case */ - BLI_strncpy(to, from, OP_MAX_TYPENAME); - } - } - else { - to[0] = 0; + const char *sep = strchr(src, '.'); + int from_len; + if (sep && (from_len = strlen(src)) < OP_MAX_TYPENAME - 3) { + const int ofs = (sep - src); + memcpy(dst, src, sizeof(char) * ofs); + BLI_str_toupper_ascii(dst, ofs); + memcpy(dst + ofs, "_OT_", 4); + memcpy(dst + (ofs + 4), sep + 1, (from_len - ofs)); + return (from_len - ofs) - 1; } + /* Should not happen but support just in case. */ + return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME); } bool WM_operator_py_idname_ok_or_report(ReportList *reports, @@ -1275,6 +1266,7 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i { Main *bmain = CTX_data_main(C); ID *id = NULL; + /* check input variables */ if (RNA_struct_property_is_set(op->ptr, "filepath")) { const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); @@ -1312,19 +1304,29 @@ ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short i } } } + + return id; } - else if (RNA_struct_property_is_set(op->ptr, "name")) { - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "name", name); - id = BKE_libblock_find_name(bmain, idcode, name); - if (!id) { + + /* Lookup an already existing ID. */ + id = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, idcode); + + if (!id) { + /* Print error with the name if the name is available. */ + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); BKE_reportf( op->reports, RPT_ERROR, "%s '%s' not found", BKE_idtype_idcode_to_name(idcode), name); return NULL; } - id_us_plus(id); + + BKE_reportf(op->reports, RPT_ERROR, "%s not found", BKE_idtype_idcode_to_name(idcode)); + return NULL; } + id_us_plus(id); return id; } @@ -1868,7 +1870,14 @@ static void WM_OT_call_menu(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_menutype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); } static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -1900,7 +1909,14 @@ static void WM_OT_call_menu_pie(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; - RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu"); + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_menutype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); } static int wm_call_panel_exec(bContext *C, wmOperator *op) @@ -1936,6 +1952,11 @@ static void WM_OT_call_panel(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu"); + RNA_def_property_string_search_func_runtime( + prop, + WM_paneltype_idname_visit_for_search, + /* Only a suggestion as menu items may be referenced from add-ons that have been disabled. */ + (PROP_STRING_SEARCH_SORT | PROP_STRING_SEARCH_SUGGESTION)); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/windowmanager/intern/wm_panel_type.c b/source/blender/windowmanager/intern/wm_panel_type.c index bfcc86c38e7..860b53c1071 100644 --- a/source/blender/windowmanager/intern/wm_panel_type.c +++ b/source/blender/windowmanager/intern/wm_panel_type.c @@ -65,3 +65,21 @@ void WM_paneltype_clear(void) { BLI_ghash_free(g_paneltypes_hash, NULL, NULL); } + +void WM_paneltype_idname_visit_for_search(const bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + const char *UNUSED(edit_text), + StringPropertySearchVisitFunc visit_fn, + void *visit_user_data) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, g_paneltypes_hash) { + PanelType *pt = BLI_ghashIterator_getValue(&gh_iter); + + StringPropertySearchVisitParams visit_params = {NULL}; + visit_params.text = pt->idname; + visit_params.info = pt->label; + visit_fn(visit_user_data, &visit_params); + } +} diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index 6750e7a7d77..a83415c98af 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -391,13 +391,20 @@ void WM_xr_action_binding_destroy(wmXrData *xr, xr->runtime->context, action_set_name, 1, &action_name, &profile_path); } -bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) +bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool delayed) { wmXrActionSet *action_set = action_set_find(xr, action_set_name); if (!action_set) { return false; } + if (delayed) { + /* Save name to activate action set later, before next actions sync + * (see #wm_xr_session_actions_update()). */ + strcpy(xr->runtime->session_state.active_action_set_next, action_set_name); + return true; + } + { /* Clear any active modal/haptic actions. */ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 3fc1f362541..bb24514457d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -54,9 +54,11 @@ typedef struct wmXrSessionState { ListBase controllers; /* #wmXrController */ /** The currently active action set that will be updated on calls to - * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and + * #wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and * updated. */ struct wmXrActionSet *active_action_set; + /* Name of the action set (if any) to activate before the next actions sync. */ + char active_action_set_next[64]; /* MAX_NAME */ } wmXrSessionState; typedef struct wmXrRuntimeData { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 0a76fd0a25f..a4d2a65830f 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1025,6 +1025,10 @@ static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); strcpy(data->action_set, action_set_name); strcpy(data->action, action->name); + strcpy(data->user_path, action->subaction_paths[subaction_idx]); + if (bimanual) { + strcpy(data->user_path_other, action->subaction_paths[subaction_idx_other]); + } data->type = action->type; switch (action->type) { @@ -1171,7 +1175,6 @@ void wm_xr_session_actions_update(wmWindowManager *wm) XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; wmXrSessionState *state = &xr->runtime->session_state; - wmXrActionSet *active_action_set = state->active_action_set; if (state->is_navigation_dirty) { memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev)); @@ -1188,6 +1191,13 @@ void wm_xr_session_actions_update(wmWindowManager *wm) &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); } + /* Set active action set if requested previously. */ + if (state->active_action_set_next[0]) { + WM_xr_active_action_set_set(xr, state->active_action_set_next, false); + state->active_action_set_next[0] = '\0'; + } + wmXrActionSet *active_action_set = state->active_action_set; + const bool synced = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); if (!synced) { |