diff options
Diffstat (limited to 'source')
297 files changed, 6747 insertions, 3569 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 9efa64d1474..4482e13e1cf 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -5,6 +5,7 @@ #include "BLI_array.hh" #include "BLI_color.hh" #include "BLI_cpp_type.hh" +#include "BLI_math_color.hh" #include "BLI_math_vector.h" #include "BLI_math_vector.hh" @@ -18,17 +19,23 @@ namespace blender::attribute_math { template<typename Func> inline void convert_to_static_type(const CPPType &cpp_type, const Func &func) { - cpp_type.to_static_type_tag<float, float2, float3, int, bool, int8_t, ColorGeometry4f>( - [&](auto type_tag) { - using T = typename decltype(type_tag)::type; - if constexpr (std::is_same_v<T, void>) { - /* It's expected that the given cpp type is one of the supported ones. */ - BLI_assert_unreachable(); - } - else { - func(T()); - } - }); + cpp_type.to_static_type_tag<float, + float2, + float3, + int, + bool, + int8_t, + ColorGeometry4f, + ColorGeometry4b>([&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_same_v<T, void>) { + /* It's expected that the given cpp type is one of the supported ones. */ + BLI_assert_unreachable(); + } + else { + func(T()); + } + }); } template<typename Func> @@ -91,6 +98,22 @@ inline ColorGeometry4f mix3(const float3 &weights, return result; } +template<> +inline ColorGeometry4b mix3(const float3 &weights, + const ColorGeometry4b &v0, + const ColorGeometry4b &v1, + const ColorGeometry4b &v2) +{ + const float4 v0_f{&v0.r}; + const float4 v1_f{&v1.r}; + const float4 v2_f{&v2.r}; + const float4 mixed = v0_f * weights[0] + v1_f * weights[1] + v2_f * weights[2]; + return ColorGeometry4b{static_cast<uint8_t>(mixed[0]), + static_cast<uint8_t>(mixed[1]), + static_cast<uint8_t>(mixed[2]), + static_cast<uint8_t>(mixed[3])}; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -134,9 +157,13 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3 template<> inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b) { - ColorGeometry4f result; - interp_v4_v4v4(result, a, b, factor); - return result; + return math::interpolate(a, b, factor); +} + +template<> +inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b) +{ + return math::interpolate(a, b, factor); } /** \} */ @@ -278,19 +305,33 @@ class SimpleMixerWithAccumulationType { } }; -class ColorGeometryMixer { +class ColorGeometry4fMixer { private: MutableSpan<ColorGeometry4f> buffer_; ColorGeometry4f default_color_; Array<float> total_weights_; public: - ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer, - ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f); void finalize(); }; +class ColorGeometry4bMixer { + private: + MutableSpan<ColorGeometry4b> buffer_; + ColorGeometry4b default_color_; + Array<float> total_weights_; + Array<float4> accumulation_buffer_; + + public: + ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255)); + void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f); + void finalize(); +}; + template<typename T> struct DefaultMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = void; @@ -307,7 +348,10 @@ template<> struct DefaultMixerStruct<float3> { template<> struct DefaultMixerStruct<ColorGeometry4f> { /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not * something one should usually do with colors. */ - using type = ColorGeometryMixer; + using type = ColorGeometry4fMixer; +}; +template<> struct DefaultMixerStruct<ColorGeometry4b> { + using type = ColorGeometry4bMixer; }; template<> struct DefaultMixerStruct<int> { static int double_to_int(const double &value) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 282e2a40bd0..6e4d4d560f7 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -451,7 +451,7 @@ bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> h /** * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector - * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuitity from the previous and next + * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuities from the previous and next * evaluated segments is assumed not to be desired. */ bool point_is_sharp(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right, int index); diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 2070584a8a0..06feb07aef2 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -195,8 +195,8 @@ enum { G_DEBUG_XR = (1 << 19), /* XR/OpenXR messages */ G_DEBUG_XR_TIME = (1 << 20), /* XR/OpenXR timing messages */ - G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */ - G_DEBUG_WINTAB = (1 << 22), /* Debug Wintab. */ + G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */ + G_DEBUG_WINTAB = (1 << 22), /* Debug Wintab. */ }; #define G_DEBUG_ALL \ diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index fff3b2a8f89..42d0e66cf49 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -414,6 +414,8 @@ int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], float r_uv[2], float r_ofs[2]); +void BKE_image_get_tile_uv(const struct Image *ima, const int tile_number, float r_uv[2]); + /** * Return the tile_number for the closest UDIM tile. */ diff --git a/source/blender/blenkernel/BKE_mesh_remap.h b/source/blender/blenkernel/BKE_mesh_remap.h index 395fc5c7eba..e2b16a7681d 100644 --- a/source/blender/blenkernel/BKE_mesh_remap.h +++ b/source/blender/blenkernel/BKE_mesh_remap.h @@ -178,6 +178,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(int mode, int numverts_dst, bool dirty_nors_dst, struct Mesh *me_src, + struct Mesh *me_dst, MeshPairRemap *r_map); void BKE_mesh_remap_calc_edges_from_mesh(int mode, @@ -190,6 +191,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(int mode, int numedges_dst, bool dirty_nors_dst, struct Mesh *me_src, + struct Mesh *me_dst, MeshPairRemap *r_map); void BKE_mesh_remap_calc_loops_from_mesh(int mode, diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 4ae37095411..f0488e84091 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -615,9 +615,6 @@ typedef struct SculptSession { union { struct { struct SculptVertexPaintGeomMap gmap; - - /* For non-airbrush painting to re-apply from the original (MLoop aligned). */ - unsigned int *previous_color; } vpaint; struct { @@ -734,6 +731,7 @@ enum { }; /* paint_canvas.cc */ + /** * Create a key that can be used to compare with previous ones to identify changes. * The resulting 'string' is owned by the caller. diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index bb918fcfdcb..978e52d8003 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -143,8 +143,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, int cd_face_node_offset); void BKE_pbvh_build_pixels(PBVH *pbvh, - const struct MLoop *mloop, - struct CustomData *ldata, + struct Mesh *mesh, struct Image *image, struct ImageUser *image_user); void BKE_pbvh_free(PBVH *pbvh); diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh index 35eb340d0a1..fdfb5fa037e 100644 --- a/source/blender/blenkernel/BKE_pbvh_pixels.hh +++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh @@ -21,7 +21,7 @@ namespace blender::bke::pbvh::pixels { struct TrianglePaintInput { int3 vert_indices; /** - * Delta barycentric coordinates between 2 neighbouring UV's in the U direction. + * Delta barycentric coordinates between 2 neighboring UV's in the U direction. * * Only the first two coordinates are stored. The third should be recalculated */ diff --git a/source/blender/blenkernel/BKE_type_conversions.hh b/source/blender/blenkernel/BKE_type_conversions.hh index 5152989d137..1cca172e188 100644 --- a/source/blender/blenkernel/BKE_type_conversions.hh +++ b/source/blender/blenkernel/BKE_type_conversions.hh @@ -2,6 +2,7 @@ #pragma once +#include "FN_field.hh" #include "FN_multi_function.hh" namespace blender::bke { @@ -59,8 +60,8 @@ class DataTypeConversions { void convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const; GVArray try_convert(GVArray varray, const CPPType &to_type) const; - GVMutableArray try_convert(GVMutableArray varray, const CPPType &to_type) const; + fn::GField try_convert(fn::GField field, const CPPType &to_type) const; }; const DataTypeConversions &get_implicit_type_conversions(); diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index 77f01e7919d..bc578ef8b28 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -77,6 +77,11 @@ const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volum /* Tries to find a grid with the given name. Make sure that the volume has been loaded. */ const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name); +/* Tries to set the name of the velocity field. If no such grid exists with the given base name, + * this will try common post-fixes in order to detect velocity fields split into multiple grids. + * Return false if neither finding with the base name nor with the post-fixes succeeded. */ +bool BKE_volume_set_velocity_grid_by_name(struct Volume *volume, const char *base_name); + /* Grid * * By default only grid metadata is loaded, for access to the tree and voxels diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 33f0c331e46..8a0ad4b724b 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -734,7 +734,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, const bool use_deform, const bool need_mapping, const CustomData_MeshMasks *dataMask, - const int index, const bool use_cache, const bool allow_shared_mesh, /* return args */ @@ -848,12 +847,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, else { break; } - - /* grab modifiers until index i */ - if ((index != -1) && (BLI_findindex(&ob->modifiers, md) >= index)) { - md = nullptr; - break; - } } /* Result of all leading deforming modifiers is cached for @@ -1129,11 +1122,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, isPrevDeform = (mti->type == eModifierTypeType_OnlyDeform); - /* grab modifiers until index i */ - if ((index != -1) && (BLI_findindex(&ob->modifiers, md) >= index)) { - break; - } - if (sculpt_mode && md->type == eModifierType_Multires) { multires_applied = true; } @@ -1611,7 +1599,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, const CustomData_MeshMasks *dataMask, const bool need_mapping) { -#if 0 /* XXX This is already taken care of in mesh_calc_modifiers()... */ +#if 0 /* XXX This is already taken care of in #mesh_calc_modifiers... */ if (need_mapping) { /* Also add the flag so that it is recorded in lastDataMask. */ dataMask->vmask |= CD_MASK_ORIGINDEX; @@ -1628,7 +1616,6 @@ static void mesh_build_data(struct Depsgraph *depsgraph, true, need_mapping, dataMask, - -1, true, true, &mesh_deform_eval, @@ -1745,13 +1732,13 @@ static void object_get_datamask(const Depsgraph *depsgraph, /* check if we need tfaces & mcols due to face select or texture paint */ if ((ob->mode & OB_MODE_TEXTURE_PAINT) || editing) { - r_mask->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; + r_mask->lmask |= CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR; r_mask->fmask |= CD_MASK_MTFACE; } /* check if we need mcols due to vertex paint or weightpaint */ if (ob->mode & OB_MODE_VERTEX_PAINT) { - r_mask->lmask |= CD_MASK_MLOOPCOL; + r_mask->lmask |= CD_MASK_PROP_BYTE_COLOR; } if (ob->mode & OB_MODE_WEIGHT_PAINT) { @@ -1882,7 +1869,7 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, true, false, dataMask, -1, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, true, false, dataMask, false, false, nullptr, &result, nullptr); return result; } @@ -1893,7 +1880,7 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, false, false, dataMask, -1, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr); return result; } @@ -1904,7 +1891,7 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, false, false, dataMask, -1, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr); return result; } diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 54fee079947..b886722676b 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1958,11 +1958,12 @@ static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot BLI_assert(lower_necs->length == blended_necs->length); } -/** Check each remap domain of blended values individually in case animator had a non-combine NLA +/** + * Check each remap domain of blended values individually in case animator had a non-combine NLA * strip with a subset of quaternion channels and remapping through any of them failed and thus * potentially has undefined values. * - * \returns true if case occured and handled. Returns false if case didn't occur. + * \returns true if case occurred and handled. Returns false if case didn't occur. */ static bool nlaevalchan_combine_quaternion_handle_undefined_blend_values( NlaEvalChannelSnapshot *blended_necs, NlaEvalChannelSnapshot *upper_or_lower_necs) @@ -1992,8 +1993,10 @@ static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelS memcpy(dst->values, src->values, src->length * sizeof(float)); } -/** Copies from lower necs to blended necs if upper necs is NULL or has zero influence. - * \return true if copied. */ +/** + * Copies from lower necs to blended necs if upper necs is NULL or has zero influence. + * \return true if copied. + */ static bool nlaevalchan_blendOrcombine_try_copy_from_lower(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *upper_necs, const float upper_influence, @@ -2008,12 +2011,14 @@ static bool nlaevalchan_blendOrcombine_try_copy_from_lower(NlaEvalChannelSnapsho return true; } -/** Copies to lower necs from blended necs if upper necs is NULL or has zero influence. If +/** + * Copies to lower necs from blended necs if upper necs is NULL or has zero influence. If * successful, copies blended_necs remap domains to lower_necs. * * Does not check upper value blend domains. * - * \return true if copied. */ + * \return true if copied. + */ static bool nlaevalchan_blendOrcombine_try_copy_to_lower(NlaEvalChannelSnapshot *blended_necs, NlaEvalChannelSnapshot *upper_necs, const float upper_influence, @@ -2033,7 +2038,8 @@ static bool nlaevalchan_blendOrcombine_try_copy_to_lower(NlaEvalChannelSnapshot return true; } -/** Based on blendmode, blend lower necs with upper necs into blended necs. +/** + * Based on blendmode, blend lower necs with upper necs into blended necs. * * Each upper value's blend domain determines whether to blend or to copy directly * from lower. @@ -2133,7 +2139,6 @@ static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs, * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode. * \param upper_influence: Value in range [0, 1]. * \param upper_necs: Never NULL. - * */ static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs, NlaEvalChannelSnapshot *upper_necs, diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 307868ce6d9..0cb0704ff80 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -311,6 +311,20 @@ AttributeDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *lay int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) { + /* When in mesh editmode, attributes point to bmesh customdata layers, the attribute data is + * empty since custom data is stored per element instead of a single array there (same es UVs + * etc.), see D11998. */ + switch (GS(id->name)) { + case ID_ME: { + Mesh *mesh = (Mesh *)id; + if (mesh->edit_mesh != NULL) { + return 0; + } + } + default: + break; + } + DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -622,10 +636,10 @@ CustomDataLayer *BKE_id_attributes_color_find(const ID *id, const char *name) layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER); } if (layer == NULL) { - layer = BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_POINT); + layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_POINT); } if (layer == NULL) { - layer = BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_CORNER); + layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_CORNER); } return layer; } diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 0ae9fa4356b..77c7857a528 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -70,11 +70,11 @@ static int attribute_data_type_complexity(const CustomDataType data_type) return 4; case CD_PROP_FLOAT3: return 5; - case CD_PROP_COLOR: + case CD_PROP_BYTE_COLOR: return 6; + case CD_PROP_COLOR: + return 7; #if 0 /* These attribute types are not supported yet. */ - case CD_MLOOPCOL: - return 3; case CD_PROP_STRING: return 6; #endif diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index bfc4c8fcde0..8c021ed0e21 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -127,7 +127,7 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | - CD_MASK_PROP_INT8; + CD_MASK_PROP_INT8 | CD_MASK_PROP_BYTE_COLOR; const AttributeDomain domain_; const CustomDataAccessInfo custom_data_access_; diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index df3cab474cd..c38df2a2969 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -4,8 +4,8 @@ namespace blender::attribute_math { -ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer, - ColorGeometry4f default_color) +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> output_buffer, + ColorGeometry4f default_color) : buffer_(output_buffer), default_color_(default_color), total_weights_(output_buffer.size(), 0.0f) @@ -13,9 +13,9 @@ ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffe buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); } -void ColorGeometryMixer::mix_in(const int64_t index, - const ColorGeometry4f &color, - const float weight) +void ColorGeometry4fMixer::mix_in(const int64_t index, + const ColorGeometry4f &color, + const float weight) { BLI_assert(weight >= 0.0f); ColorGeometry4f &output_color = buffer_[index]; @@ -26,7 +26,7 @@ void ColorGeometryMixer::mix_in(const int64_t index, total_weights_[index] += weight; } -void ColorGeometryMixer::finalize() +void ColorGeometry4fMixer::finalize() { for (const int64_t i : buffer_.index_range()) { const float weight = total_weights_[i]; @@ -44,4 +44,43 @@ void ColorGeometryMixer::finalize() } } +ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + ColorGeometry4b default_color) + : buffer_(buffer), + default_color_(default_color), + total_weights_(buffer.size(), 0.0f), + accumulation_buffer_(buffer.size(), float4(0, 0, 0, 0)) +{ +} + +void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, float weight) +{ + BLI_assert(weight >= 0.0f); + float4 &accum_value = accumulation_buffer_[index]; + accum_value[0] += color.r * weight; + accum_value[1] += color.g * weight; + accum_value[2] += color.b * weight; + accum_value[3] += color.a * weight; + total_weights_[index] += weight; +} + +void ColorGeometry4bMixer::finalize() +{ + for (const int64_t i : buffer_.index_range()) { + const float weight = total_weights_[i]; + const float4 &accum_value = accumulation_buffer_[i]; + ColorGeometry4b &output_color = buffer_[i]; + if (weight > 0.0f) { + const float weight_inv = 1.0f / weight; + output_color.r = accum_value[0] * weight_inv; + output_color.g = accum_value[1] * weight_inv; + output_color.b = accum_value[2] * weight_inv; + output_color.a = accum_value[3] * weight_inv; + } + else { + output_color = default_color_; + } + } +} + } // namespace blender::attribute_math diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index b9cd9e1ee59..0593db34e20 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1557,8 +1557,10 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush) if (brush->curves_sculpt_settings == NULL) { brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__); } - brush->curves_sculpt_settings->add_amount = 1; - brush->curves_sculpt_settings->minimum_length = 0.01f; + BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; + settings->add_amount = 1; + settings->minimum_length = 0.01f; + settings->curve_length = 0.3f; } struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index f7454a02de6..0d6a0c045a5 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -1171,6 +1171,7 @@ static Mesh *cloth_make_rest_mesh(ClothModifierData *clmd, Mesh *mesh) for (unsigned i = 0; i < mesh->totvert; i++, verts++) { copy_v3_v3(mvert[i].co, verts->xrest); } + BKE_mesh_normals_tag_dirty(new_mesh); return new_mesh; } @@ -1507,7 +1508,6 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) if (clmd->sim_parms->shapekey_rest && !(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH)) { tmp_mesh = cloth_make_rest_mesh(clmd, mesh); - BKE_mesh_calc_normals(tmp_mesh); } EdgeSet *existing_vert_pairs = BLI_edgeset_new("cloth_sewing_edges_graph"); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index bad70d4ccc6..4338883853d 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -113,9 +113,12 @@ static void curve_free_data(ID *id) BKE_curve_batch_cache_free(curve); BKE_nurbList_free(&curve->nurb); - BKE_curve_editfont_free(curve); - BKE_curve_editNurb_free(curve); + if (!curve->edit_data_from_original) { + BKE_curve_editfont_free(curve); + + BKE_curve_editNurb_free(curve); + } BKE_curveprofile_free(curve->bevel_profile); diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index c48d155f5ce..d7fd8f7a2b6 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -649,7 +649,6 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); mesh->flag |= ME_AUTOSMOOTH; mesh->smoothresh = DEG2RADF(180.0f); - BKE_mesh_normals_tag_dirty(mesh); MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop); diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 6dd9460aaa9..22db90a06b0 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -765,7 +765,12 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) } } -/* --------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MLoopCol, #CD_PROP_BYTE_COLOR) + * \{ */ + static void layerCopyValue_mloopcol(const void *source, void *dest, const int mixmode, @@ -954,6 +959,12 @@ static int layerMaxNum_mloopcol() return MAX_MCOL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks for (#MLoopUV, #CD_MLOOPUV) + * \{ */ + static void layerCopyValue_mloopuv(const void *source, void *dest, const int mixmode, @@ -1716,7 +1727,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, nullptr, layerMaxNum_tface}, - /* 17: CD_MLOOPCOL */ + /* 17: CD_PROP_BYTE_COLOR */ {sizeof(MLoopCol), "MLoopCol", 1, @@ -2052,46 +2063,45 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = { }; const CustomData_MeshMasks CD_MASK_MESH = { /* vmask */ (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | - CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), + CD_MASK_PROP_ALL | CD_MASK_CREASE), /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), /* fmask */ 0, /* pmask */ (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ - (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | - CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), + (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | + CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), }; const CustomData_MeshMasks CD_MASK_DERIVEDMESH = { /* vmask */ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL | - CD_MASK_PROP_COLOR | CD_MASK_CREASE), + CD_MASK_CREASE), /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), /* fmask */ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT), /* pmask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ - (CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_PREVIEW_MLOOPCOL | + (CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PROP_ALL), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */ }; const CustomData_MeshMasks CD_MASK_BMESH = { /* vmask */ (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY | - CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | - CD_MASK_PROP_COLOR | CD_MASK_CREASE), + CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE), /* emask */ (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), /* fmask */ 0, /* pmask */ (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ - (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL | - CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), + (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | + CD_MASK_PROP_ALL), }; const CustomData_MeshMasks CD_MASK_EVERYTHING = { /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | - CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), + CD_MASK_PROP_ALL | CD_MASK_CREASE), /* emask */ (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), @@ -2104,9 +2114,8 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = { CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ (CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV | - CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_MLOOPTANGENT | - CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_GRID_PAINT_MASK | - CD_MASK_PROP_ALL), + CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_MLOOPTANGENT | CD_MASK_PREVIEW_MLOOPCOL | + CD_MASK_ORIGSPACE_MLOOP | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), }; static const LayerTypeInfo *layerType_getInfo(int type) @@ -3486,7 +3495,7 @@ void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *ldata, int totloop) } else if (fdata->layers[i].type == CD_MCOL) { CustomData_add_layer_named( - ldata, CD_MLOOPCOL, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); + ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, totloop, fdata->layers[i].name); } else if (fdata->layers[i].type == CD_MDISPS) { CustomData_add_layer_named( @@ -3509,7 +3518,7 @@ void CustomData_from_bmeshpoly(CustomData *fdata, CustomData *ldata, int total) CustomData_add_layer_named( fdata, CD_MTFACE, CD_CALLOC, nullptr, total, ldata->layers[i].name); } - if (ldata->layers[i].type == CD_MLOOPCOL) { + if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) { CustomData_add_layer_named(fdata, CD_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name); } else if (ldata->layers[i].type == CD_PREVIEW_MLOOPCOL) { @@ -3544,7 +3553,7 @@ bool CustomData_from_bmeshpoly_test(CustomData *fdata, CustomData *ldata, bool f if (!LAYER_CMP(ldata, CD_MLOOPUV, fdata, CD_MTFACE)) { return false; } - if (!LAYER_CMP(ldata, CD_MLOOPCOL, fdata, CD_MCOL)) { + if (!LAYER_CMP(ldata, CD_PROP_BYTE_COLOR, fdata, CD_MCOL)) { return false; } if (!LAYER_CMP(ldata, CD_PREVIEW_MLOOPCOL, fdata, CD_PREVIEW_MCOL)) { @@ -3586,17 +3595,17 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) CustomData_set_layer_stencil(fdata, CD_MTFACE, act); } - if (CustomData_has_layer(ldata, CD_MLOOPCOL)) { - act = CustomData_get_active_layer(ldata, CD_MLOOPCOL); + if (CustomData_has_layer(ldata, CD_PROP_BYTE_COLOR)) { + act = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); CustomData_set_layer_active(fdata, CD_MCOL, act); - act = CustomData_get_render_layer(ldata, CD_MLOOPCOL); + act = CustomData_get_render_layer(ldata, CD_PROP_BYTE_COLOR); CustomData_set_layer_render(fdata, CD_MCOL, act); - act = CustomData_get_clone_layer(ldata, CD_MLOOPCOL); + act = CustomData_get_clone_layer(ldata, CD_PROP_BYTE_COLOR); CustomData_set_layer_clone(fdata, CD_MCOL, act); - act = CustomData_get_stencil_layer(ldata, CD_MLOOPCOL); + act = CustomData_get_stencil_layer(ldata, CD_PROP_BYTE_COLOR); CustomData_set_layer_stencil(fdata, CD_MCOL, act); } } @@ -3621,16 +3630,16 @@ void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, Custom if (CustomData_has_layer(fdata, CD_MCOL)) { act = CustomData_get_active_layer(fdata, CD_MCOL); - CustomData_set_layer_active(ldata, CD_MLOOPCOL, act); + CustomData_set_layer_active(ldata, CD_PROP_BYTE_COLOR, act); act = CustomData_get_render_layer(fdata, CD_MCOL); - CustomData_set_layer_render(ldata, CD_MLOOPCOL, act); + CustomData_set_layer_render(ldata, CD_PROP_BYTE_COLOR, act); act = CustomData_get_clone_layer(fdata, CD_MCOL); - CustomData_set_layer_clone(ldata, CD_MLOOPCOL, act); + CustomData_set_layer_clone(ldata, CD_PROP_BYTE_COLOR, act); act = CustomData_get_stencil_layer(fdata, CD_MCOL); - CustomData_set_layer_stencil(ldata, CD_MLOOPCOL, act); + CustomData_set_layer_stencil(ldata, CD_PROP_BYTE_COLOR, act); } } @@ -5401,6 +5410,8 @@ const blender::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) return &CPPType::get<bool>(); case CD_PROP_INT8: return &CPPType::get<int8_t>(); + case CD_PROP_BYTE_COLOR: + return &CPPType::get<ColorGeometry4b>(); default: return nullptr; } @@ -5430,6 +5441,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) if (type.is<int8_t>()) { return CD_PROP_INT8; } + if (type.is<ColorGeometry4b>()) { + return CD_PROP_BYTE_COLOR; + } return static_cast<CustomDataType>(-1); } diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 2369ce88ebc..6a3f6a47f5e 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -217,7 +217,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) return CD_FAKE_LNOR; case DT_TYPE_MLOOPCOL_VERT: case DT_TYPE_MLOOPCOL_LOOP: - return CD_MLOOPCOL; + return CD_PROP_BYTE_COLOR; case DT_TYPE_MPROPCOL_VERT: case DT_TYPE_MPROPCOL_LOOP: return CD_PROP_COLOR; @@ -1501,6 +1501,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, num_verts_dst, dirty_nors_dst, me_src, + me_dst, &geom_map[VDATA]); geom_map_init[VDATA] = true; } @@ -1579,6 +1580,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, num_edges_dst, dirty_nors_dst, me_src, + me_dst, &geom_map[EDATA]); geom_map_init[EDATA] = true; } diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 8c1161d6ff6..63aa03483b2 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -887,17 +887,11 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, if (mti->type == eModifierTypeType_OnlyDeform) { int totvert; float(*vertex_coords)[3] = BKE_mesh_vert_coords_alloc(mesh, &totvert); - if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { - BKE_mesh_vertex_normals_ensure(mesh); - } mti->deformVerts(md, &mectx_deform, mesh, vertex_coords, totvert); BKE_mesh_vert_coords_apply(mesh, vertex_coords); MEM_freeN(vertex_coords); } else { - if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { - BKE_mesh_vertex_normals_ensure(mesh); - } Mesh *output_mesh = mti->modifyMesh(md, &mectx_apply, mesh); if (mesh != output_mesh) { geometry_set.replace_mesh(output_mesh); @@ -1472,10 +1466,12 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, const bool for_render) { BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)); - Curve &cow_curve = *(Curve *)ob->data; BKE_object_free_derived_caches(ob); - cow_curve.curve_eval = nullptr; + + /* It's important to retrieve this after calling #BKE_object_free_derived_caches, + * which may reset the object data pointer in some cases. */ + const Curve &original_curve = *static_cast<const Curve *>(ob->data); ob->runtime.curve_cache = MEM_cnew<CurveCache>(__func__); ListBase *dispbase = &ob->runtime.curve_cache->disp; @@ -1488,14 +1484,27 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase); if (geometry.has_curves()) { - /* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval - * added to the curve here, it will also contain a copy of the original curve's data. This is - * essential, because it maintains the expected behavior for evaluated curve data from before - * the CurveEval data type was introduced, when an evaluated object's curve data was just a - * copy of the original curve and everything else ended up in #CurveCache. */ - CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>(); - cow_curve.curve_eval = curve_component.get_for_read(); - BKE_object_eval_assign_data(ob, &cow_curve.id, false); + /* Create a copy of the original curve and add necessary pointers to evaluated and edit mode + * data. This is needed for a few reasons: + * - Existing code from before curve evaluation was changed to use #GeometrySet expected to + * have a copy of the original curve data. (Any evaluated data was placed in + * #Object.runtime.curve_cache). + * - The result of modifier evaluation is not a #Curve data-block but a #Curves data-block, + * which can support constructive modifiers and geometry nodes. + * - The dependency graph has handling of edit mode pointers (see #update_edit_mode_pointers) + * but it doesn't seem to work in this case. + * + * Since the the plan is to replace this legacy curve object with the curves data-block + * (see T95355), this somewhat hacky inefficient solution is relatively temporary. + */ + Curve &cow_curve = *reinterpret_cast<Curve *>( + BKE_id_copy_ex(nullptr, &original_curve.id, nullptr, LIB_ID_COPY_LOCALIZE)); + cow_curve.curve_eval = geometry.get_curves_for_read(); + /* Copy edit mode pointers necessary for drawing to the duplicated curve. */ + cow_curve.editnurb = original_curve.editnurb; + cow_curve.editfont = original_curve.editfont; + cow_curve.edit_data_from_original = true; + BKE_object_eval_assign_data(ob, &cow_curve.id, true); } ob->runtime.geometry_set_eval = new GeometrySet(std::move(geometry)); diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index b85b2b3157c..22341f98375 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -336,7 +336,7 @@ bool dynamicPaint_outputLayerExists(struct DynamicPaintSurface *surface, Object if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { Mesh *me = ob->data; - return (CustomData_get_named_layer_index(&me->ldata, CD_MLOOPCOL, name) != -1); + return (CustomData_get_named_layer_index(&me->ldata, CD_PROP_BYTE_COLOR, name) != -1); } if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { return (BKE_object_defgroup_name_index(ob, name) != -1); @@ -1664,7 +1664,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface const MLoop *mloop = mesh->mloop; const int totloop = mesh->totloop; const MLoopCol *col = CustomData_get_layer_named( - &mesh->ldata, CD_MLOOPCOL, surface->init_layername); + &mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername); if (!col) { return; } @@ -1676,7 +1676,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( - &mesh->ldata, CD_MLOOPCOL, surface->init_layername); + &mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername); if (!col) { return; } @@ -1899,7 +1899,6 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * pmd->type == MOD_DYNAMICPAINT_TYPE_CANVAS) { DynamicPaintSurface *surface; - bool update_normals = false; /* loop through surfaces */ for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { @@ -1941,20 +1940,28 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * /* paint layer */ MLoopCol *mloopcol = CustomData_get_layer_named( - &result->ldata, CD_MLOOPCOL, surface->output_name); + &result->ldata, CD_PROP_BYTE_COLOR, surface->output_name); /* if output layer is lost from a constructive modifier, re-add it */ if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) { - mloopcol = CustomData_add_layer_named( - &result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name); + mloopcol = CustomData_add_layer_named(&result->ldata, + CD_PROP_BYTE_COLOR, + CD_CALLOC, + NULL, + totloop, + surface->output_name); } /* wet layer */ MLoopCol *mloopcol_wet = CustomData_get_layer_named( - &result->ldata, CD_MLOOPCOL, surface->output_name2); + &result->ldata, CD_PROP_BYTE_COLOR, surface->output_name2); /* if output layer is lost from a constructive modifier, re-add it */ if (!mloopcol_wet && dynamicPaint_outputLayerExists(surface, ob, 1)) { - mloopcol_wet = CustomData_add_layer_named( - &result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, totloop, surface->output_name2); + mloopcol_wet = CustomData_add_layer_named(&result->ldata, + CD_PROP_BYTE_COLOR, + CD_CALLOC, + NULL, + totloop, + surface->output_name2); } data.ob = ob; @@ -2018,21 +2025,17 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * settings.use_threading = (sData->total_points > 1000); BLI_task_parallel_range( 0, sData->total_points, &data, dynamic_paint_apply_surface_wave_cb, &settings); - update_normals = true; + BKE_mesh_normals_tag_dirty(mesh); } /* displace */ if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) { dynamicPaint_applySurfaceDisplace(surface, result); - update_normals = true; + BKE_mesh_normals_tag_dirty(mesh); } } } } - - if (update_normals) { - BKE_mesh_normals_tag_dirty(result); - } } /* make a copy of mesh to use as brush data */ else if (pmd->brush && pmd->type == MOD_DYNAMICPAINT_TYPE_BRUSH) { diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 81e73b6cf2c..5de13fbdbed 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3537,7 +3537,6 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje } BKE_mesh_calc_edges(result, false, false); - BKE_mesh_normals_tag_dirty(result); return result; } @@ -5071,6 +5070,9 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, tfds->openvdb_compression = fds->openvdb_compression; tfds->clipping = fds->clipping; tfds->openvdb_data_depth = fds->openvdb_data_depth; + + /* Render options. */ + tfds->velocity_scale = fds->velocity_scale; } else if (tfmd->flow) { FluidFlowSettings *tffs = tfmd->flow; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 47f7bb00b8f..fb39861d3e7 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -903,22 +903,6 @@ static void set_loop_uv(MLoopUV &uv, float2 co) copy_v2_v2(uv.uv, co); } -static ColorGeometry4f get_loop_color(const MLoopCol &col) -{ - ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a); - ColorGeometry4f linear_color = encoded_color.decode(); - return linear_color; -} - -static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color) -{ - ColorGeometry4b encoded_color = linear_color.encode(); - col.r = encoded_color.r; - col.g = encoded_color.g; - col.b = encoded_color.b; - col.a = encoded_color.a; -} - static float get_crease(const MEdge &edge) { return edge.crease / 255.0f; @@ -1293,14 +1277,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() make_derived_read_attribute<MLoopUV, float2, get_loop_uv>, make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>); - static NamedLegacyCustomDataProvider vertex_colors( - ATTR_DOMAIN_CORNER, - CD_PROP_COLOR, - CD_MLOOPCOL, - corner_access, - make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>, - make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>); - static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); @@ -1310,7 +1286,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() return ComponentAttributeProviders( {&position, &id, &material_index, &shade_smooth, &normal, &crease}, {&uvs, - &vertex_colors, &corner_custom_data, &vertex_groups, &point_custom_data, diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 53ec148fd2d..dfa820519a5 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -829,10 +829,7 @@ ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser)); } -int BKE_image_get_tile_from_pos(struct Image *ima, - const float uv[2], - float r_uv[2], - float r_ofs[2]) +int BKE_image_get_tile_from_pos(Image *ima, const float uv[2], float r_uv[2], float r_ofs[2]) { float local_ofs[2]; if (r_ofs == nullptr) { @@ -860,6 +857,18 @@ int BKE_image_get_tile_from_pos(struct Image *ima, return tile_number; } +void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2]) +{ + if (ima->source != IMA_SRC_TILED) { + zero_v2(r_uv); + } + else { + const int tile_index = tile_number - 1001; + r_uv[0] = static_cast<float>(tile_index % 10); + r_uv[1] = static_cast<float>(tile_index / 10); + } +} + int BKE_image_find_nearest_tile(const Image *image, const float co[2]) { const float co_floor[2] = {floorf(co[0]), floorf(co[1])}; @@ -868,17 +877,15 @@ int BKE_image_find_nearest_tile(const Image *image, const float co[2]) int tile_number_best = -1; LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { - const int tile_index = tile->tile_number - 1001; - /* Coordinates of the current tile. */ - const float tile_index_co[2] = {static_cast<float>(tile_index % 10), - static_cast<float>(tile_index / 10)}; + float uv_offset[2]; + BKE_image_get_tile_uv(image, tile->tile_number, uv_offset); - if (equals_v2v2(co_floor, tile_index_co)) { + if (equals_v2v2(co_floor, uv_offset)) { return tile->tile_number; } /* Distance between co[2] and UDIM tile. */ - const float dist_sq = len_squared_v2v2(tile_index_co, co); + const float dist_sq = len_squared_v2v2(uv_offset, co); if (dist_sq < dist_best_sq) { dist_best_sq = dist_sq; diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 4caaf314888..002b496393f 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1943,6 +1943,8 @@ static void material_default_gpencil_init(Material *ma) static void material_default_surface_init(Material *ma) { + strcpy(ma->id.name, "MADefault Surface"); + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); ma->nodetree = ntree; ma->use_nodes = true; @@ -1969,6 +1971,8 @@ static void material_default_surface_init(Material *ma) static void material_default_volume_init(Material *ma) { + strcpy(ma->id.name, "MADefault Volume"); + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); ma->nodetree = ntree; ma->use_nodes = true; @@ -1992,6 +1996,8 @@ static void material_default_volume_init(Material *ma) static void material_default_holdout_init(Material *ma) { + strcpy(ma->id.name, "MADefault Holdout"); + bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname); ma->nodetree = ntree; ma->use_nodes = true; diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 25d97d0bd3c..c13a2bc794a 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -463,7 +463,8 @@ static int customdata_compare( CustomDataLayer *l1, *l2; int layer_count1 = 0, layer_count2 = 0, j; const uint64_t cd_mask_non_generic = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | - CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MDEFORMVERT; + CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR | + CD_MASK_MDEFORMVERT; const uint64_t cd_mask_all_attr = CD_MASK_PROP_ALL | cd_mask_non_generic; for (int i = 0; i < c1->totlayer; i++) { @@ -581,7 +582,7 @@ static int customdata_compare( } break; } - case CD_MLOOPCOL: { + case CD_PROP_BYTE_COLOR: { MLoopCol *lp1 = (MLoopCol *)l1->data; MLoopCol *lp2 = (MLoopCol *)l2->data; int ltot = m1->totloop; @@ -768,7 +769,7 @@ static void mesh_ensure_tessellation_customdata(Mesh *me) } else { const int tottex_original = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - const int totcol_original = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + const int totcol_original = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR); const int tottex_tessface = CustomData_number_of_layers(&me->fdata, CD_MTFACE); const int totcol_tessface = CustomData_number_of_layers(&me->fdata, CD_MCOL); @@ -786,7 +787,8 @@ static void mesh_ensure_tessellation_customdata(Mesh *me) * some info to help troubleshoot what's going on. */ printf( "%s: warning! Tessellation uvs or vcol data got out of sync, " - "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != CD_MLOOPCOL: " + "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != " + "CD_PROP_BYTE_COLOR: " "%d\n", __func__, tottex_tessface, @@ -871,7 +873,7 @@ bool BKE_mesh_clear_facemap_customdata(struct Mesh *me) /** * This ensures grouped custom-data (e.g. #CD_MLOOPUV and #CD_MTFACE, or - * #CD_MLOOPCOL and #CD_MCOL) have the same relative active/render/clone/mask indices. + * #CD_PROP_BYTE_COLOR and #CD_MCOL) have the same relative active/render/clone/mask indices. * * NOTE(@campbellbarton): that for undo mesh data we want to skip 'ensure_tess_cd' call since * we don't want to store memory for #MFace data when its only used for older @@ -902,7 +904,7 @@ void BKE_mesh_update_customdata_pointers(Mesh *me, const bool do_ensure_tess_cd) me->mpoly = (MPoly *)CustomData_get_layer(&me->pdata, CD_MPOLY); me->mloop = (MLoop *)CustomData_get_layer(&me->ldata, CD_MLOOP); - me->mloopcol = (MLoopCol *)CustomData_get_layer(&me->ldata, CD_MLOOPCOL); + me->mloopcol = (MLoopCol *)CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); me->mloopuv = (MLoopUV *)CustomData_get_layer(&me->ldata, CD_MLOOPUV); } @@ -1112,6 +1114,9 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, mesh_ensure_cdlayers_primary(me_dst, do_tessface); BKE_mesh_update_customdata_pointers(me_dst, false); + /* Expect that normals aren't copied at all, since the destination mesh is new. */ + BLI_assert(BKE_mesh_vertex_normals_are_dirty(me_dst)); + return me_dst; } @@ -1688,6 +1693,7 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys) mul_m3_v3(m3, *lnors); } } + BKE_mesh_normals_tag_dirty(me); } void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys) diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index eee1d3b9eec..3fcacb31b24 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -782,7 +782,6 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim) } } - BKE_mesh_calc_normals(result); if (dbg_level > 0) { BKE_mesh_validate(result, true, true); } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index ff953ef5b46..a289d208684 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -120,9 +120,6 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me) } BKE_mesh_update_customdata_pointers(me, true); - - BKE_mesh_normals_tag_dirty(me); - BKE_mesh_calc_edges(me, true, false); } } @@ -514,7 +511,6 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * } mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); - BKE_mesh_normals_tag_dirty(mesh); if (totvert != 0) { memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index 6c5a5de31fc..ec2660a0145 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -661,7 +661,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_MLOOPCOL, loopstart, 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); MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]); diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 2e634a14872..3a37c29c1d0 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -478,6 +478,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, const int numverts_dst, const bool UNUSED(dirty_nors_dst), Mesh *me_src, + Mesh *me_dst, MeshPairRemap *r_map) { const float full_weight = 1.0f; @@ -580,7 +581,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, MPoly *polys_src = me_src->mpoly; MLoop *loops_src = me_src->mloop; float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, NULL); - const float(*vert_normals_src)[3] = BKE_mesh_vertex_normals_ensure(me_src); + const float(*vert_normals_dst)[3] = BKE_mesh_vertex_normals_ensure(me_dst); size_t tmp_buff_size = MREMAP_DEFAULT_BUFSIZE; float(*vcos)[3] = MEM_mallocN(sizeof(*vcos) * tmp_buff_size, __func__); @@ -592,7 +593,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode, if (mode == MREMAP_MODE_VERT_POLYINTERP_VNORPROJ) { for (i = 0; i < numverts_dst; i++) { copy_v3_v3(tmp_co, verts_dst[i].co); - copy_v3_v3(tmp_no, vert_normals_src[i]); + copy_v3_v3(tmp_no, vert_normals_dst[i]); /* Convert the vertex to tree coordinates, if needed. */ if (space_transform) { @@ -703,6 +704,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, const int numedges_dst, const bool UNUSED(dirty_nors_dst), Mesh *me_src, + Mesh *me_dst, MeshPairRemap *r_map) { const float full_weight = 1.0f; @@ -938,7 +940,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_EDGES, 2); - const float(*vert_normals_dst)[3] = BKE_mesh_vertex_normals_ensure(me_src); + const float(*vert_normals_dst)[3] = BKE_mesh_vertex_normals_ensure(me_dst); for (i = 0; i < numedges_dst; i++) { /* For each dst edge, we sample some rays from it (interpolated from its vertices) diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 7be4a6f2f94..02c9f61957d 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -140,7 +140,6 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh, } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); MEM_freeN(qrd.out_faces); MEM_freeN(qrd.out_verts); @@ -257,7 +256,6 @@ static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index ae52e31cb9b..ea3cc043267 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -58,7 +58,7 @@ static void mesh_loops_to_tessdata(CustomData *fdata, * The issue is, unless having two different functions with nearly the same code, * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */ const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); - const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + const int numCol = CustomData_number_of_layers(ldata, CD_PROP_BYTE_COLOR); const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); @@ -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_MLOOPCOL, i); + 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--;) { diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 6af765d7de5..d16f7eaf588 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -1017,7 +1017,7 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata, pdata, mask.pmask, totpoly, do_verbose, do_fixes, &is_change_p); const int tot_uvloop = CustomData_number_of_layers(ldata, CD_MLOOPUV); - const int tot_vcolloop = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + const int tot_vcolloop = CustomData_number_of_layers(ldata, CD_PROP_BYTE_COLOR); if (tot_uvloop > MAX_MTFACE) { PRINT_ERR( "\tMore UV layers than %d allowed, %d last ones won't be available for render, shaders, " diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index f4703b32582..e7ee8caf0eb 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -944,8 +944,10 @@ static void modwrap_dependsOnNormals(Mesh *me) break; } case ME_WRAPPER_TYPE_SUBD: + /* Not an expected case. */ + break; case ME_WRAPPER_TYPE_MDATA: - BKE_mesh_calc_normals(me); + /* Normals are calculated lazily. */ break; } } @@ -992,7 +994,7 @@ void BKE_modifier_deform_vertsEM(ModifierData *md, { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (me && mti->dependsOnNormals && mti->dependsOnNormals(md)) { - BKE_mesh_calc_normals(me); + modwrap_dependsOnNormals(me); } mti->deformVertsEM(md, ctx, em, me, vertexCos, numVerts); } diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c index 65ba7b64d83..837b64affa8 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c @@ -156,7 +156,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape /* Vertices were moved around, need to update normals after all the vertices are updated * Probably this is possible to do in the loop above, but this is rather tricky because * we don't know all needed vertices' coordinates there yet. */ - BKE_mesh_calc_normals(base_mesh); + BKE_mesh_normals_tag_dirty(base_mesh); } void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshape_context) diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 5ff1f6b950f..948064ad170 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1789,6 +1789,7 @@ void BKE_object_free_derived_caches(Object *ob) BKE_mesh_eval_delete((Mesh *)data_eval); } else { + BKE_libblock_free_data(data_eval, false); BKE_libblock_free_datablock(data_eval, 0); MEM_freeN(data_eval); } @@ -3564,19 +3565,19 @@ void BKE_object_apply_parent_inverse(struct Object *ob) * Use parent's world transform as the child's origin. * * Let: - * local = identity - * world = orthonormalized(parent) + * `local = identity` + * `world = orthonormalized(parent)` * * Then: - * world = parent @ parentinv @ local - * inv(parent) @ world = parentinv - * parentinv = inv(parent) @ world + * `world = parent @ parentinv @ local` + * `inv(parent) @ world = parentinv` + * `parentinv = inv(parent) @ world` * - * NOTE: If ob->obmat has shear, then this `parentinv` is insufficient because - * parent @ parentinv => shearless result + * NOTE: If `ob->obmat` has shear, then this `parentinv` is insufficient because + * `parent @ parentinv => shearless result` * * Thus, local will have shear which cannot be decomposed into TRS: - * local = inv(parent @ parentinv) @ world + * `local = inv(parent @ parentinv) @ world` * * This is currently not supported for consistency in the handling of shear during the other * parenting ops: Parent (Keep Transform), Clear [Parent] and Keep Transform. @@ -3591,11 +3592,11 @@ void BKE_object_apply_parent_inverse(struct Object *ob) /* Now, preserve `world` given the new `parentinv`. * - * world = parent @ parentinv @ local - * inv(parent) @ world = parentinv @ local - * inv(parentinv) @ inv(parent) @ world = local + * `world = parent @ parentinv @ local` + * `inv(parent) @ world = parentinv @ local` + * `inv(parentinv) @ inv(parent) @ world = local` * - * local = inv(parentinv) @ inv(parent) @ world + * `local = inv(parentinv) @ inv(parent) @ world` */ float ob_local[4][4]; copy_m4_m4(ob_local, ob->parentinv); @@ -3974,8 +3975,9 @@ bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const Re } if (visibility_flag & OB_EMPTY_IMAGE_HIDE_NON_AXIS_ALIGNED) { - float3 proj; - project_plane_v3_v3v3(proj, ob->obmat[2], rv3d->viewinv[2]); + float3 proj, ob_z_axis; + normalize_v3_v3(ob_z_axis, ob->obmat[2]); + project_plane_v3_v3v3(proj, ob_z_axis, rv3d->viewinv[2]); const float proj_length_sq = len_squared_v3(proj); if (proj_length_sq > 1e-5f) { return false; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index fbbd429f58a..f41d59c77ba 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -167,7 +167,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o #endif if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) { /* Always compute UVs, vertex colors as orcos for render. */ - cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; + cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR; cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR; } makeDerivedMesh(depsgraph, scene, ob, &cddata_masks); /* was CD_MASK_BAREMESH */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 5fd7984ea90..0f523d87d9b 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1101,7 +1101,6 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) } else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__); - data->curve_length = 0.3f; paint = &data->paint; } else if (*r_paint == &ts->imapaint.paint) { @@ -1341,8 +1340,6 @@ void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss) struct SculptVertexPaintGeomMap *gmap = NULL; if (ss->mode_type == OB_MODE_VERTEX_PAINT) { gmap = &ss->mode.vpaint.gmap; - - MEM_SAFE_FREE(ss->mode.vpaint.previous_color); } else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) { gmap = &ss->mode.wpaint.gmap; @@ -1845,7 +1842,7 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object) { Mesh *orig_me = BKE_object_get_original_mesh(object); - int types[] = {CD_PROP_COLOR, CD_MLOOPCOL}; + int types[] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; bool has_color = false; for (int i = 0; i < ARRAY_SIZE(types); i++) { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index e91f360ef22..1fb1570b3ff 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1267,7 +1267,7 @@ bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, Attribu { CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)me); - if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_MLOOPCOL)) { + if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) { *r_layer = NULL; *r_attr = ATTR_DOMAIN_NUM; return false; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index d32a03186e3..be6e95426c2 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -50,7 +50,7 @@ inline void to_static_color_type(const CustomDataType type, const Func &func) case CD_PROP_COLOR: func(MPropCol()); break; - case CD_MLOOPCOL: + case CD_PROP_BYTE_COLOR: func(MLoopCol()); break; default: diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 6a135c248ce..55d76938ad3 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -1240,7 +1240,7 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, EDGE_QUEUE_DISABLE(e); #endif - /* At the moment edges never get shorter (subdiv will make new edges) + /* At the moment edges never get shorter (subdivision will make new edges) * unlike collapse where edges can become longer. */ #if 0 if (len_squared_v3v3(v1->co, v2->co) <= eq_ctx->q->limit_len_squared) { diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 77bd00da50a..b7a2b578a1c 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -267,6 +267,7 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode); /* pbvh_pixels.hh */ + void pbvh_pixels_free(PBVHNode *node); void pbvh_pixels_free_brush_test(PBVHNode *node); diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index d8dd2f4b382..5623cac44ac 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -22,12 +22,14 @@ namespace blender::bke::pbvh::pixels { -/** Durind debugging this check could be enabled. It will write to each image pixel that is covered - * by the pbvh. */ +/** + * During debugging this check could be enabled. + * It will write to each image pixel that is covered by the PBVH. + */ constexpr bool USE_WATERTIGHT_CHECK = false; /** - * Calculate the delta of two neighbour uv coordinates in the given image buffer. + * Calculate the delta of two neighbor UV coordinates in the given image buffer. */ static float2 calc_barycentric_delta(const float2 uvs[3], const float2 start_uv, @@ -273,11 +275,7 @@ static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_us BKE_image_partial_update_mark_full_update(image); } -static void update_pixels(PBVH *pbvh, - const struct MLoop *mloop, - struct CustomData *ldata, - struct Image *image, - struct ImageUser *image_user) +static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) { Vector<PBVHNode *> nodes_to_update; @@ -285,14 +283,14 @@ static void update_pixels(PBVH *pbvh, return; } - MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(ldata, CD_MLOOPUV)); + MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); if (ldata_uv == nullptr) { return; } for (PBVHNode *node : nodes_to_update) { NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data); - init_triangles(pbvh, node, node_data, mloop); + init_triangles(pbvh, node, node_data, mesh->mloop); } EncodePixelsUserData user_data; @@ -375,13 +373,9 @@ void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &i extern "C" { using namespace blender::bke::pbvh::pixels; -void BKE_pbvh_build_pixels(PBVH *pbvh, - const struct MLoop *mloop, - struct CustomData *ldata, - struct Image *image, - struct ImageUser *image_user) +void BKE_pbvh_build_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user) { - update_pixels(pbvh, mloop, ldata, image, image_user); + update_pixels(pbvh, mesh, image, image_user); } void pbvh_pixels_free(PBVHNode *node) diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index 83e4336e3b1..83427adcb43 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -1155,7 +1155,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, * calculation. Since vertex normals are supposed to be a consistent cache, don't bother * calculating them here. The work may have been pointless anyway if the mesh is deformed or * changed afterwards. */ - BKE_mesh_normals_tag_dirty(result); + BLI_assert(BKE_mesh_vertex_normals_are_dirty(result) || BKE_mesh_poly_normals_are_dirty(result)); /* Free used memory. */ subdiv_mesh_context_free(&subdiv_context); return result; diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index e84ec5b3890..1c2665769db 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -2,6 +2,8 @@ #include "BKE_type_conversions.hh" +#include "DNA_meshdata_types.h" + #include "FN_multi_function_builder.hh" #include "BLI_color.hh" @@ -61,6 +63,10 @@ static ColorGeometry4f float_to_color(const float &a) { return ColorGeometry4f(a, a, a, 1.0f); } +static ColorGeometry4b float_to_byte_color(const float &a) +{ + return float_to_color(a).encode(); +} static float3 float2_to_float3(const float2 &a) { @@ -86,6 +92,10 @@ static ColorGeometry4f float2_to_color(const float2 &a) { return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); } +static ColorGeometry4b float2_to_byte_color(const float2 &a) +{ + return float2_to_color(a).encode(); +} static bool float3_to_bool(const float3 &a) { @@ -111,6 +121,10 @@ static ColorGeometry4f float3_to_color(const float3 &a) { return ColorGeometry4f(a.x, a.y, a.z, 1.0f); } +static ColorGeometry4b float3_to_byte_color(const float3 &a) +{ + return float3_to_color(a).encode(); +} static bool int_to_bool(const int32_t &a) { @@ -137,6 +151,10 @@ static ColorGeometry4f int_to_color(const int32_t &a) { return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } +static ColorGeometry4b int_to_byte_color(const int32_t &a) +{ + return int_to_color(a).encode(); +} static bool int8_to_bool(const int8_t &a) { @@ -162,6 +180,10 @@ static ColorGeometry4f int8_to_color(const int8_t &a) { return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } +static ColorGeometry4b int8_to_byte_color(const int8_t &a) +{ + return int8_to_color(a).encode(); +} static float bool_to_float(const bool &a) { @@ -187,6 +209,10 @@ static ColorGeometry4f bool_to_color(const bool &a) { return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); } +static ColorGeometry4b bool_to_byte_color(const bool &a) +{ + return bool_to_color(a).encode(); +} static bool color_to_bool(const ColorGeometry4f &a) { @@ -212,6 +238,39 @@ static float3 color_to_float3(const ColorGeometry4f &a) { return float3(a.r, a.g, a.b); } +static ColorGeometry4b color_to_byte_color(const ColorGeometry4f &a) +{ + return a.encode(); +} + +static bool byte_color_to_bool(const ColorGeometry4b &a) +{ + return a.r > 0 || a.g > 0 || a.b > 0; +} +static float byte_color_to_float(const ColorGeometry4b &a) +{ + return color_to_float(a.decode()); +} +static int32_t byte_color_to_int(const ColorGeometry4b &a) +{ + return color_to_int(a.decode()); +} +static int8_t byte_color_to_int8(const ColorGeometry4b &a) +{ + return color_to_int8(a.decode()); +} +static float2 byte_color_to_float2(const ColorGeometry4b &a) +{ + return color_to_float2(a.decode()); +} +static float3 byte_color_to_float3(const ColorGeometry4b &a) +{ + return color_to_float3(a.decode()); +} +static ColorGeometry4f byte_color_to_color(const ColorGeometry4b &a) +{ + return a.decode(); +} static DataTypeConversions create_implicit_conversions() { @@ -223,6 +282,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float, bool, float_to_bool>(conversions); add_implicit_conversion<float, int8_t, float_to_int8>(conversions); add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); + add_implicit_conversion<float, ColorGeometry4b, float_to_byte_color>(conversions); add_implicit_conversion<float2, float3, float2_to_float3>(conversions); add_implicit_conversion<float2, float, float2_to_float>(conversions); @@ -230,6 +290,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float2, bool, float2_to_bool>(conversions); add_implicit_conversion<float2, int8_t, float2_to_int8>(conversions); add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); + add_implicit_conversion<float2, ColorGeometry4b, float2_to_byte_color>(conversions); add_implicit_conversion<float3, bool, float3_to_bool>(conversions); add_implicit_conversion<float3, int8_t, float3_to_int8>(conversions); @@ -237,6 +298,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); add_implicit_conversion<float3, float2, float3_to_float2>(conversions); add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); + add_implicit_conversion<float3, ColorGeometry4b, float3_to_byte_color>(conversions); add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); add_implicit_conversion<int32_t, int8_t, int_to_int8>(conversions); @@ -244,6 +306,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); + add_implicit_conversion<int32_t, ColorGeometry4b, int_to_byte_color>(conversions); add_implicit_conversion<int8_t, bool, int8_to_bool>(conversions); add_implicit_conversion<int8_t, int32_t, int8_to_int>(conversions); @@ -251,6 +314,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<int8_t, float2, int8_to_float2>(conversions); add_implicit_conversion<int8_t, float3, int8_to_float3>(conversions); add_implicit_conversion<int8_t, ColorGeometry4f, int8_to_color>(conversions); + add_implicit_conversion<int8_t, ColorGeometry4b, int8_to_byte_color>(conversions); add_implicit_conversion<bool, float, bool_to_float>(conversions); add_implicit_conversion<bool, int8_t, bool_to_int8>(conversions); @@ -258,6 +322,7 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<bool, float2, bool_to_float2>(conversions); add_implicit_conversion<bool, float3, bool_to_float3>(conversions); add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); + add_implicit_conversion<bool, ColorGeometry4b, bool_to_byte_color>(conversions); add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); add_implicit_conversion<ColorGeometry4f, int8_t, color_to_int8>(conversions); @@ -265,6 +330,15 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions); + add_implicit_conversion<ColorGeometry4f, ColorGeometry4b, color_to_byte_color>(conversions); + + add_implicit_conversion<ColorGeometry4b, bool, byte_color_to_bool>(conversions); + add_implicit_conversion<ColorGeometry4b, int8_t, byte_color_to_int8>(conversions); + add_implicit_conversion<ColorGeometry4b, float, byte_color_to_float>(conversions); + add_implicit_conversion<ColorGeometry4b, int32_t, byte_color_to_int>(conversions); + add_implicit_conversion<ColorGeometry4b, float2, byte_color_to_float2>(conversions); + add_implicit_conversion<ColorGeometry4b, float3, byte_color_to_float3>(conversions); + add_implicit_conversion<ColorGeometry4b, ColorGeometry4f, byte_color_to_color>(conversions); return conversions; } @@ -411,4 +485,19 @@ GVMutableArray DataTypeConversions::try_convert(GVMutableArray varray, std::move(varray), to_type, *this); } +fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_type) const +{ + const CPPType &from_type = field.cpp_type(); + if (from_type == to_type) { + return field; + } + if (!this->is_convertible(from_type, to_type)) { + return {}; + } + const fn::MultiFunction &fn = + *bke::get_implicit_type_conversions().get_conversion_multi_function( + fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)); + return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})}; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 0c131863edd..307466d7dc9 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -60,6 +60,7 @@ using blender::float3; using blender::float4x4; using blender::IndexRange; using blender::StringRef; +using blender::StringRefNull; #ifdef WITH_OPENVDB # include <atomic> @@ -517,6 +518,8 @@ static void volume_init_data(ID *id) MEMCPY_STRUCT_AFTER(volume, DNA_struct_default_get(Volume), id); BKE_volume_init_grids(volume); + + BLI_strncpy(volume->velocity_grid, "velocity", sizeof(volume->velocity_grid)); } static void volume_copy_data(Main *UNUSED(bmain), @@ -794,6 +797,57 @@ bool BKE_volume_is_loaded(const Volume *volume) #endif } +bool BKE_volume_set_velocity_grid_by_name(Volume *volume, const char *base_name) +{ + const StringRefNull ref_base_name = base_name; + + if (BKE_volume_grid_find_for_read(volume, base_name)) { + BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid)); + volume->runtime.velocity_x_grid[0] = '\0'; + volume->runtime.velocity_y_grid[0] = '\0'; + volume->runtime.velocity_z_grid[0] = '\0'; + return true; + } + + /* It could be that the velocity grid is split in multiple grids, try with known postfixes. */ + const StringRefNull postfixes[][3] = {{"x", "y", "z"}, {".x", ".y", ".z"}, {"_x", "_y", "_z"}}; + + for (const StringRefNull *postfix : postfixes) { + bool found = true; + for (int i = 0; i < 3; i++) { + std::string post_fixed_name = ref_base_name + postfix[i]; + if (!BKE_volume_grid_find_for_read(volume, post_fixed_name.c_str())) { + found = false; + break; + } + } + + if (!found) { + continue; + } + + /* Save the base name as well. */ + BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid)); + BLI_strncpy(volume->runtime.velocity_x_grid, + (ref_base_name + postfix[0]).c_str(), + sizeof(volume->runtime.velocity_x_grid)); + BLI_strncpy(volume->runtime.velocity_y_grid, + (ref_base_name + postfix[1]).c_str(), + sizeof(volume->runtime.velocity_y_grid)); + BLI_strncpy(volume->runtime.velocity_z_grid, + (ref_base_name + postfix[2]).c_str(), + sizeof(volume->runtime.velocity_z_grid)); + return true; + } + + /* Reset to avoid potential issues. */ + volume->velocity_grid[0] = '\0'; + volume->runtime.velocity_x_grid[0] = '\0'; + volume->runtime.velocity_y_grid[0] = '\0'; + volume->runtime.velocity_z_grid[0] = '\0'; + return false; +} + bool BKE_volume_load(const Volume *volume, const Main *bmain) { #ifdef WITH_OPENVDB @@ -857,6 +911,14 @@ bool BKE_volume_load(const Volume *volume, const Main *bmain) } } + /* Try to detect the velocity grid. */ + const char *common_velocity_names[] = {"velocity", "vel", "v"}; + for (const char *common_velocity_name : common_velocity_names) { + if (BKE_volume_set_velocity_grid_by_name(const_cast<Volume *>(volume), common_velocity_name)) { + break; + } + } + BLI_strncpy(grids.filepath, filepath, FILE_MAX); return grids.error_msg.empty(); diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index 8544d6c9c77..ef75d3d2482 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -183,7 +183,6 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid, {mesh->mloop, mesh->totloop}); BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 0754221eb65..98fd7d0f15d 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -345,5 +345,7 @@ BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_l using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>; using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>; +using ColorPaint4f = ColorSceneLinear4f<eAlpha::Straight>; +using ColorPaint4b = ColorSceneLinearByteEncoded4b<eAlpha::Straight>; } // namespace blender diff --git a/source/blender/blenlib/BLI_color_mix.hh b/source/blender/blenlib/BLI_color_mix.hh new file mode 100644 index 00000000000..4989ddc609e --- /dev/null +++ b/source/blender/blenlib/BLI_color_mix.hh @@ -0,0 +1,1051 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup blenlib + * + * Contains color mixing utilities. + */ + +#include "BLI_color.hh" +#include "BLI_math_base.h" +#include "BLI_math_color.h" +#include "BLI_sys_types.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" + +#include <type_traits> + +namespace blender::color { + +struct ByteTraits { + using ValueType = uchar; + using BlendType = int; + + inline static const uchar range = 255; /* Zero-based maximum value. */ + inline static const float frange = 255.0f; /* Convenient floating-point version of range. */ + inline static const int cmpRange = 254; + inline static const int expandedRange = 256; /* One-based maximum value. */ + + inline static const int bytes = 1; + inline static const float unit = 255.0f; + + static inline BlendType divide_round(BlendType a, BlendType b) + { + return divide_round_i(a, b); + } + + static inline BlendType min(BlendType a, BlendType b) + { + return min_ii(a, b); + } + + static inline BlendType max(BlendType a, BlendType b) + { + return max_ii(a, b); + } + /* Discretizes in steps of 1.0 / range */ + static inline ValueType round(float f) + { + return round_fl_to_uchar(f); + } +}; + +struct FloatTraits { + using ValueType = float; + using BlendType = float; + + inline const static float range = 1.0f; + inline const static float frange = 1.0f; + inline const static float cmpRange = 0.9999f; + inline static const int expandedRange = 1.0f; + + inline const static float unit = 1.0f; + inline const static int bytes = 4; + + static inline BlendType divide_round(BlendType a, BlendType b) + { + return a / b; + } + + static inline BlendType min(BlendType a, BlendType b) + { + return min_ff(a, b); + } + + static inline BlendType max(BlendType a, BlendType b) + { + return min_ff(a, b); + } + + /* Discretizes in steps of 1.0 / range */ + static inline ValueType round(float f) + { + return f; + } +}; + +static float get_luminance(ColorPaint4f c) +{ + return IMB_colormanagement_get_luminance(&c.r); +} + +static int get_luminance(ColorPaint4b c) +{ + return IMB_colormanagement_get_luminance_byte(&c.r); +} + +#define EPS_SATURATION 0.0005f + +/* -------------------------------------------------------------------- */ +/** \name Color Blending Modes + * \{ */ + +template<typename Color, typename Traits> +static Color mix_blend(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Color col_mix(0, 0, 0, 0); + Blend mfac; + + if (fac == 0) { + return col_src; + } + + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = &col_src.r; + cp_dst = &col_dst.r; + cp_mix = &col_mix.r; + + /* Updated to use the rgb squared color model which blends nicer. */ + Blend r1 = cp_src[0] * cp_src[0]; + Blend g1 = cp_src[1] * cp_src[1]; + Blend b1 = cp_src[2] * cp_src[2]; + Blend a1 = cp_src[3] * cp_src[3]; + + Blend r2 = cp_dst[0] * cp_dst[0]; + Blend g2 = cp_dst[1] * cp_dst[1]; + Blend b2 = cp_dst[2] * cp_dst[2]; + Blend a2 = cp_dst[3] * cp_dst[3]; + + cp_mix[0] = Traits::round(sqrtf(Traits::divide_round((mfac * r1 + fac * r2), Traits::range))); + cp_mix[1] = Traits::round(sqrtf(Traits::divide_round((mfac * g1 + fac * g2), Traits::range))); + cp_mix[2] = Traits::round(sqrtf(Traits::divide_round((mfac * b1 + fac * b2), Traits::range))); + cp_mix[3] = Traits::round(sqrtf(Traits::divide_round((mfac * a1 + fac * a2), Traits::range))); + return Color(col_mix[0], col_mix[1], col_mix[2], col_mix[3]); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_add(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src.r; + cp_dst = (Value *)&col_dst.r; + cp_mix = (Value *)&col_mix.r; + + temp = cp_src[0] + Traits::divide_round((fac * cp_dst[0]), Traits::range); + cp_mix[0] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[1] + Traits::divide_round((fac * cp_dst[1]), Traits::range); + cp_mix[1] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[2] + Traits::divide_round((fac * cp_dst[2]), Traits::range); + cp_mix[2] = (temp > Traits::cmpRange) ? Traits::range : temp; + temp = cp_src[3] + Traits::divide_round((fac * cp_dst[3]), Traits::range); + cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_sub(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend temp; + Color col_mix(0, 0, 0, 0); + + cp_src = (Value *)&col_src.r; + cp_dst = (Value *)&col_dst.r; + cp_mix = (Value *)&col_mix.r; + + temp = cp_src[0] - Traits::divide_round((fac * cp_dst[0]), Traits::range); + cp_mix[0] = (temp < 0) ? 0 : temp; + temp = cp_src[1] - Traits::divide_round((fac * cp_dst[1]), Traits::range); + cp_mix[1] = (temp < 0) ? 0 : temp; + temp = cp_src[2] - Traits::divide_round((fac * cp_dst[2]), Traits::range); + cp_mix[2] = (temp < 0) ? 0 : temp; + temp = cp_src[3] - Traits::divide_round((fac * cp_dst[3]), Traits::range); + cp_mix[3] = (temp < 0) ? 0 : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_mul(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* first mul, then blend the fac */ + cp_mix[0] = Traits::divide_round(mfac * cp_src[0] * Traits::range + fac * cp_dst[0] * cp_src[0], + Traits::range * Traits::range); + cp_mix[1] = Traits::divide_round(mfac * cp_src[1] * Traits::range + fac * cp_dst[1] * cp_src[1], + Traits::range * Traits::range); + cp_mix[2] = Traits::divide_round(mfac * cp_src[2] * Traits::range + fac * cp_dst[2] * cp_src[2], + Traits::range * Traits::range); + cp_mix[3] = Traits::divide_round(mfac * cp_src[3] * Traits::range + fac * cp_dst[3] * cp_src[3], + Traits::range * Traits::range); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_lighten(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* See if we're lighter, if so mix, else don't do anything. + * if the paint color is darker then the original, then ignore */ + if (get_luminance(cp_src) > get_luminance(cp_dst)) { + return col_src; + } + + cp_mix[0] = Traits::divide_round(mfac * cp_src[0] + fac * cp_dst[0], Traits::range); + cp_mix[1] = Traits::divide_round(mfac * cp_src[1] + fac * cp_dst[1], Traits::range); + cp_mix[2] = Traits::divide_round(mfac * cp_src[2] + fac * cp_dst[2], Traits::range); + cp_mix[3] = Traits::divide_round(mfac * cp_src[3] + fac * cp_dst[3], Traits::range); + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_darken(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + if (fac >= Traits::range) { + return col_dst; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* See if we're darker, if so mix, else don't do anything. + * if the paint color is brighter then the original, then ignore */ + if (get_luminance(cp_src) < get_luminance(cp_dst)) { + return col_src; + } + + cp_mix[0] = Traits::divide_round((mfac * cp_src[0] + fac * cp_dst[0]), Traits::range); + cp_mix[1] = Traits::divide_round((mfac * cp_src[1] + fac * cp_dst[1]), Traits::range); + cp_mix[2] = Traits::divide_round((mfac * cp_src[2] + fac * cp_dst[2]), Traits::range); + cp_mix[3] = Traits::divide_round((mfac * cp_src[3] + fac * cp_dst[3]), Traits::range); + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_colordodge(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + Blend dodgefac = (Blend)((float)Traits::range * 0.885f); /* ~225/255 */ + + temp = (cp_dst[0] == Traits::range) ? + Traits::range : + Traits::min((cp_src[0] * dodgefac) / (Traits::range - cp_dst[0]), Traits::range); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = (cp_dst[1] == Traits::range) ? + Traits::range : + Traits::min((cp_src[1] * dodgefac) / (Traits::range - cp_dst[1]), Traits::range); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = (cp_dst[2] == Traits::range) ? + Traits::range : + Traits::min((cp_src[2] * dodgefac) / (Traits::range - cp_dst[2]), Traits::range); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = (cp_dst[3] == Traits::range) ? + Traits::range : + Traits::min((cp_src[3] * dodgefac) / (Traits::range - cp_dst[3]), Traits::range); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_difference(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + temp = abs(cp_src[0] - cp_dst[0]); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = abs(cp_src[1] - cp_dst[1]); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = abs(cp_src[2] - cp_dst[2]); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = abs(cp_src[3] - cp_dst[3]); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_screen(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + temp = Traits::max(Traits::range - (((Traits::range - cp_src[0]) * (Traits::range - cp_dst[0])) / + Traits::range), + 0); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[1]) * (Traits::range - cp_dst[1])) / + Traits::range), + 0); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[2]) * (Traits::range - cp_dst[2])) / + Traits::range), + 0); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / Traits::range; + temp = Traits::max(Traits::range - (((Traits::range - cp_src[3]) * (Traits::range - cp_dst[3])) / + Traits::range), + 0); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_hardlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_dst[i] > (Traits::range / 2)) { + temp = Traits::range - ((Traits::range - 2 * (cp_dst[i] - (Traits::range / 2))) * + (Traits::range - cp_src[i]) / Traits::range); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange; + } + cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range); + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_overlay(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_src[i] > (Traits::range / 2)) { + temp = Traits::range - ((Traits::range - 2 * (cp_src[i] - (Traits::range / 2))) * + (Traits::range - cp_dst[i]) / Traits::range); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) / Traits::expandedRange; + } + cp_mix[i] = Traits::min((mfac * cp_src[i] + temp * fac) / Traits::range, Traits::range); + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_softlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + /* Use divide_round so we don't alter original byte equations. */ + const int add = Traits::divide_round(Traits::range, 4); + + for (int i = 0; i < 4; i++) { + if (cp_src[i] < (Traits::range / 2)) { + temp = ((2 * ((cp_dst[i] / 2) + add)) * cp_src[i]) / Traits::range; + } + else { + temp = Traits::range - (2 * (Traits::range - ((cp_dst[i] / 2) + add)) * + (Traits::range - cp_src[i]) / Traits::range); + } + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range; + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_exclusion(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac, temp; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + temp = (Traits::range / 2) - + ((2 * (cp_src[i] - (Traits::range / 2)) * (cp_dst[i] - (Traits::range / 2))) / + Traits::range); + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / Traits::range; + } + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_luminosity(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + v1 = v2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_saturation(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + if (s1 > EPS_SATURATION) { + s1 = s2; + } + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_hue(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + h1 = h2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = ((Blend)(r * Traits::frange) * fac + mfac * cp_src[0]) / Traits::range; + cp_mix[1] = ((Blend)(g * Traits::frange) * fac + mfac * cp_src[1]) / Traits::range; + cp_mix[2] = ((Blend)(b * Traits::frange) * fac + mfac * cp_src[2]) / Traits::range; + cp_mix[3] = ((Blend)(cp_dst[3]) * fac + mfac * cp_src[3]) / Traits::range; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_alpha_add(Color col_src, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_mix; + Blend temp; + Color col_mix = col_src; + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src; + cp_mix = (Value *)&col_mix; + + temp = cp_src[3] + fac; + cp_mix[3] = (temp > Traits::cmpRange) ? Traits::range : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_alpha_sub(Color col_src, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_mix; + Blend temp; + Color col_mix = col_src; + + if (fac == 0) { + return col_src; + } + + cp_src = (Value *)&col_src; + cp_mix = (Value *)&col_mix; + + temp = cp_src[3] - fac; + cp_mix[3] = temp < 0 ? 0 : temp; + + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_pinlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + const Blend cmp = Traits::range / 2; + + int i = 3; + Blend temp; + + while (i--) { + if (cp_dst[i] > cmp) { + temp = Traits::max(2 * (cp_dst[i] - cmp), cp_src[i]); + } + else { + temp = Traits::min(2 * cp_dst[i], cp_src[i]); + } + cp_mix[i] = (Value)((Traits::min(temp, Traits::range) * fac + cp_src[i] * mfac) / + Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_linearlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + const Blend cmp = Traits::range / 2; + + int i = 3; + while (i--) { + Blend temp; + + if (cp_dst[i] > cmp) { + temp = Traits::min(cp_src[i] + 2 * (cp_dst[i] - cmp), Traits::range); + } + else { + temp = Traits::max(cp_src[i] + 2 * cp_dst[i] - Traits::range, 0); + } + + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_vividlight(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + + const Blend cmp = Traits::range / 2; + + int i = 3; + + while (i--) { + Blend temp; + + if (cp_dst[i] == Traits::range) { + temp = (cp_src[i] == 0) ? cmp : Traits::range; + } + else if (cp_dst[i] == 0) { + temp = (cp_src[i] == Traits::range) ? cmp : 0; + } + else if (cp_dst[i] > cmp) { + temp = Traits::min(((cp_src[i]) * Traits::range) / (2 * (Traits::range - cp_dst[i])), + Traits::range); + } + else { + temp = Traits::max( + Traits::range - ((Traits::range - cp_src[i]) * Traits::range / (2 * cp_dst[i])), 0); + } + col_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_color(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + + rgb_to_hsv(cp_src[0] / Traits::frange, + cp_src[1] / Traits::frange, + cp_src[2] / Traits::frange, + &h1, + &s1, + &v1); + rgb_to_hsv(cp_dst[0] / Traits::frange, + cp_dst[1] / Traits::frange, + cp_dst[2] / Traits::frange, + &h2, + &s2, + &v2); + + h1 = h2; + s1 = s2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + cp_mix[0] = (Value)(((Blend)(r * Traits::frange) * fac + cp_src[0] * mfac) / Traits::range); + cp_mix[1] = (Value)(((Blend)(g * Traits::frange) * fac + cp_src[1] * mfac) / Traits::range); + cp_mix[2] = (Value)(((Blend)(b * Traits::frange) * fac + cp_src[2] * mfac) / Traits::range); + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_colorburn(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 3; + + while (i--) { + const Blend temp = + (cp_dst[i] == 0) ? + 0 : + Traits::max(Traits::range - ((Traits::range - cp_src[i]) * Traits::range) / cp_dst[i], + 0); + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +static Color mix_linearburn(Color col_src, Color col_dst, typename Traits::BlendType fac) +{ + using Value = typename Traits::ValueType; + using Blend = typename Traits::BlendType; + + Value *cp_src, *cp_dst, *cp_mix; + Blend mfac; + Color col_mix(0, 0, 0, 0); + + if (fac == 0) { + return col_src; + } + + mfac = Traits::range - fac; + + cp_src = (Value *)&col_src; + cp_dst = (Value *)&col_dst; + cp_mix = (Value *)&col_mix; + + int i = 3; + + while (i--) { + const Blend temp = Traits::max(cp_src[i] + cp_dst[i] - Traits::range, 0); + cp_mix[i] = (Value)((temp * fac + cp_src[i] * mfac) / Traits::range); + } + + col_mix.a = col_src.a; + return col_mix; +} + +template<typename Color, typename Traits> +BLI_INLINE Color BLI_mix_colors(const IMB_BlendMode tool, + const Color a, + const Color b, + const typename Traits::BlendType alpha) +{ + switch ((IMB_BlendMode)tool) { + case IMB_BLEND_MIX: + return mix_blend<Color, Traits>(a, b, alpha); + case IMB_BLEND_ADD: + return mix_add<Color, Traits>(a, b, alpha); + case IMB_BLEND_SUB: + return mix_sub<Color, Traits>(a, b, alpha); + case IMB_BLEND_MUL: + return mix_mul<Color, Traits>(a, b, alpha); + case IMB_BLEND_LIGHTEN: + return mix_lighten<Color, Traits>(a, b, alpha); + case IMB_BLEND_DARKEN: + return mix_darken<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLORDODGE: + return mix_colordodge<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLORBURN: + return mix_colorburn<Color, Traits>(a, b, alpha); + case IMB_BLEND_DIFFERENCE: + return mix_difference<Color, Traits>(a, b, alpha); + case IMB_BLEND_SCREEN: + return mix_screen<Color, Traits>(a, b, alpha); + case IMB_BLEND_HARDLIGHT: + return mix_hardlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_OVERLAY: + return mix_overlay<Color, Traits>(a, b, alpha); + case IMB_BLEND_SOFTLIGHT: + return mix_softlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_EXCLUSION: + return mix_exclusion<Color, Traits>(a, b, alpha); + case IMB_BLEND_LUMINOSITY: + return mix_luminosity<Color, Traits>(a, b, alpha); + case IMB_BLEND_SATURATION: + return mix_saturation<Color, Traits>(a, b, alpha); + case IMB_BLEND_HUE: + return mix_hue<Color, Traits>(a, b, alpha); + /* non-color */ + case IMB_BLEND_ERASE_ALPHA: + return mix_alpha_sub<Color, Traits>(a, alpha); + case IMB_BLEND_ADD_ALPHA: + return mix_alpha_add<Color, Traits>(a, alpha); + case IMB_BLEND_PINLIGHT: + return mix_pinlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_LINEARLIGHT: + return mix_linearlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_VIVIDLIGHT: + return mix_vividlight<Color, Traits>(a, b, alpha); + case IMB_BLEND_COLOR: + return mix_color<Color, Traits>(a, b, alpha); + default: + BLI_assert(0); + return Color(0, 0, 0, 0); + } +} +/** \} */ + +} // namespace blender::color diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index 4aed1caf796..2e756e912f9 100644 --- a/source/blender/blenlib/BLI_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -854,14 +854,14 @@ template<typename T> inline GVArray::GVArray(const VArray<T> &varray) if (varray.may_have_ownership()) { *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); } - else if (varray.is_span()) { - Span<T> data = varray.get_internal_span(); - *this = GVArray::ForSpan(data); - } else if (varray.is_single()) { T value = varray.get_internal_single(); *this = GVArray::ForSingle(CPPType::get<T>(), varray.size(), &value); } + else if (varray.is_span()) { + Span<T> data = varray.get_internal_span(); + *this = GVArray::ForSpan(data); + } else { *this = GVArray::For<GVArrayImpl_For_VArray<T>>(varray); } @@ -880,15 +880,15 @@ template<typename T> inline VArray<T> GVArray::typed() const if (this->may_have_ownership()) { return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); } - if (this->is_span()) { - const Span<T> span = this->get_internal_span().typed<T>(); - return VArray<T>::ForSpan(span); - } if (this->is_single()) { T value; this->get_internal_single(&value); return VArray<T>::ForSingle(value, this->size()); } + if (this->is_span()) { + const Span<T> span = this->get_internal_span().typed<T>(); + return VArray<T>::ForSpan(span); + } return VArray<T>::template For<VArrayImpl_For_GVArray<T>>(*this); } diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh index 83f414f853a..81f5343056e 100644 --- a/source/blender/blenlib/BLI_math_base.hh +++ b/source/blender/blenlib/BLI_math_base.hh @@ -102,7 +102,10 @@ template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(cons return a - std::floor(a); } -template<typename T, typename FactorT, BLI_ENABLE_IF((is_math_float_type<FactorT>))> +template<typename T, + typename FactorT, + BLI_ENABLE_IF((std::is_arithmetic_v<T>)), + BLI_ENABLE_IF((is_math_float_type<FactorT>))> inline T interpolate(const T &a, const T &b, const FactorT &t) { return a * (1 - t) + b * t; diff --git a/source/blender/blenlib/BLI_math_color.hh b/source/blender/blenlib/BLI_math_color.hh index 5195cfb6238..b16053509cf 100644 --- a/source/blender/blenlib/BLI_math_color.hh +++ b/source/blender/blenlib/BLI_math_color.hh @@ -15,9 +15,22 @@ namespace blender::math { -inline ColorGeometry4f interpolate(const ColorGeometry4f &a, - const ColorGeometry4f &b, - const float t) +template<eAlpha Alpha> +inline ColorSceneLinear4f<Alpha> interpolate(const ColorSceneLinear4f<Alpha> &a, + const ColorSceneLinear4f<Alpha> &b, + const float t) +{ + return {math::interpolate(a.r, b.r, t), + math::interpolate(a.g, b.g, t), + math::interpolate(a.b, b.b, t), + math::interpolate(a.a, b.a, t)}; +} + +template<eAlpha Alpha> +inline ColorSceneLinearByteEncoded4b<Alpha> interpolate( + const ColorSceneLinearByteEncoded4b<Alpha> &a, + const ColorSceneLinearByteEncoded4b<Alpha> &b, + const float t) { return {math::interpolate(a.r, b.r, t), math::interpolate(a.g, b.g, t), diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index 0850864d86f..e36bbedee32 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -201,6 +201,14 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> } } + template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))> + explicit vec_base(const U *ptr) + { + for (int i = 0; i < Size; i++) { + (*this)[i] = ptr[i]; + } + } + vec_base(const T (*ptr)[Size]) : vec_base(static_cast<const T *>(ptr[0])) { } diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 27bb04f5796..453ca67b1e0 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -780,9 +780,6 @@ template<typename T> class VArrayCommon { Span<T> get_internal_span() const { BLI_assert(this->is_span()); - if (this->is_empty()) { - return {}; - } return impl_->get_internal_span(); } @@ -800,9 +797,6 @@ template<typename T> class VArrayCommon { T get_internal_single() const { BLI_assert(this->is_single()); - if (impl_->size() == 1) { - return impl_->get(0); - } return impl_->get_internal_single(); } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e8a3851e082..446a027b03b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC BLI_boxpack_2d.h BLI_buffer.h BLI_color.hh + BLI_color_mix.hh BLI_compiler_attrs.h BLI_compiler_compat.h BLI_compiler_typecheck.h diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 26a0479f445..5ca6fe2efd0 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -301,56 +301,60 @@ static bool delete_soft(const wchar_t *path_16, const char **error_message) /* Deletes file or directory to recycling bin. The latter moves all contained files and * directories recursively to the recycling bin as well. */ IFileOperation *pfo; - IShellItem *pSI; + IShellItem *psi; HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - if (FAILED(hr)) { - *error_message = "Failed to initialize COM"; - goto error_1; - } - - hr = CoCreateInstance( - &CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo); - if (FAILED(hr)) { - *error_message = "Failed to create FileOperation instance"; - goto error_2; - } - - /* Flags for deletion: - * FOF_ALLOWUNDO: Enables moving file to recycling bin. - * FOF_SILENT: Don't show progress dialog box. - * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */ - hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING); - - if (FAILED(hr)) { - *error_message = "Failed to set operation flags"; - goto error_2; - } - - hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&pSI); - if (FAILED(hr)) { - *error_message = "Failed to parse path"; - goto error_2; - } - - hr = pfo->lpVtbl->DeleteItem(pfo, pSI, NULL); - if (FAILED(hr)) { - *error_message = "Failed to prepare delete operation"; - goto error_2; + if (SUCCEEDED(hr)) { + /* This is also the case when COM was previously initialized and CoInitializeEx returns + * S_FALSE, which is not an error. Both HRESULT values S_OK and S_FALSE indicate success. */ + + hr = CoCreateInstance( + &CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo); + + if (SUCCEEDED(hr)) { + /* Flags for deletion: + * FOF_ALLOWUNDO: Enables moving file to recycling bin. + * FOF_SILENT: Don't show progress dialog box. + * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */ + hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING); + + if (SUCCEEDED(hr)) { + hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&psi); + + if (SUCCEEDED(hr)) { + hr = pfo->lpVtbl->DeleteItem(pfo, psi, NULL); + + if (SUCCEEDED(hr)) { + hr = pfo->lpVtbl->PerformOperations(pfo); + + if (FAILED(hr)) { + *error_message = "Failed to prepare delete operation"; + } + } + else { + *error_message = "Failed to prepare delete operation"; + } + psi->lpVtbl->Release(psi); + } + else { + *error_message = "Failed to parse path"; + } + } + else { + *error_message = "Failed to set operation flags"; + } + pfo->lpVtbl->Release(pfo); + } + else { + *error_message = "Failed to create FileOperation instance"; + } + CoUninitialize(); } - - hr = pfo->lpVtbl->PerformOperations(pfo); - - if (FAILED(hr)) { - *error_message = "Failed to delete file or directory"; + else { + *error_message = "Failed to initialize COM"; } -error_2: - pfo->lpVtbl->Release(pfo); - CoUninitialize(); /* Has to be uninitialized when CoInitializeEx returns either S_OK or S_FALSE - */ -error_1: return FAILED(hr); } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 655dc297c35..e4a93762da4 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -357,7 +357,7 @@ static void do_versions_mesh_mloopcol_swap_2_62_1(Mesh *me) for (a = 0; a < me->ldata.totlayer; a++) { layer = &me->ldata.layers[a]; - if (layer->type == CD_MLOOPCOL) { + if (layer->type == CD_PROP_BYTE_COLOR) { mloopcol = (MLoopCol *)layer->data; for (i = 0; i < me->totloop; i++, mloopcol++) { SWAP(uchar, mloopcol->r, mloopcol->b); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 0996b35c8ea..d992d426b9a 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1620,11 +1620,6 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) /* Deprecated, only kept for conversion. */ BKE_mesh_tessface_clear(me); - - /* Moved from do_versions because we need updated polygons for calculating normals. */ - if (!MAIN_VERSION_ATLEAST(bmain, 256, 6)) { - BKE_mesh_calc_normals(me); - } } } @@ -1931,7 +1926,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Grease pencil multiframe falloff curve */ + /* Grease pencil multi-frame falloff curve. */ if (!DNA_struct_elem_find( fd->filesdna, "GP_Sculpt_Settings", "CurveMapping", "cur_falloff")) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index ec60183218c..f0055fb73ac 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2448,6 +2448,54 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + /* Rebuild active/render color attribute references. */ + if (!MAIN_VERSION_ATLEAST(bmain, 302, 6)) { + LISTBASE_FOREACH (Brush *, br, &bmain->brushes) { + /* buggy code in wm_toolsystem broke smear in old files, + reset to defaults */ + if (br->sculpt_tool == SCULPT_TOOL_SMEAR) { + br->alpha = 1.0f; + br->spacing = 5; + br->flag &= ~BRUSH_ALPHA_PRESSURE; + br->flag &= ~BRUSH_SPACE_ATTEN; + br->curve_preset = BRUSH_CURVE_SPHERE; + } + } + + LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { + for (int step = 0; step < 2; step++) { + CustomDataLayer *actlayer = NULL; + + int vact1, vact2; + + if (step) { + vact1 = CustomData_get_render_layer_index(&me->vdata, CD_PROP_COLOR); + vact2 = CustomData_get_render_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); + } + else { + vact1 = CustomData_get_active_layer_index(&me->vdata, CD_PROP_COLOR); + vact2 = CustomData_get_active_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); + } + + if (vact1 != -1) { + actlayer = me->vdata.layers + vact1; + } + else if (vact2 != -1) { + actlayer = me->ldata.layers + vact2; + } + + if (actlayer) { + if (step) { + BKE_id_attributes_render_color_set(&me->id, actlayer); + } + else { + BKE_id_attributes_active_color_set(&me->id, actlayer); + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 302, 7)) { /* Generate 'system' liboverrides IDs. * NOTE: This is a fairly rough process, based on very basic heuristics. Should be enough for a @@ -2481,12 +2529,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__); brush->curves_sculpt_settings->add_amount = 1; } - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - if (scene->toolsettings && scene->toolsettings->curves_sculpt && - scene->toolsettings->curves_sculpt->curve_length == 0.0f) { - scene->toolsettings->curves_sculpt->curve_length = 0.3f; - } - } for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { @@ -2590,11 +2632,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (step) { vact1 = CustomData_get_render_layer_index(&me->vdata, CD_PROP_COLOR); - vact2 = CustomData_get_render_layer_index(&me->ldata, CD_MLOOPCOL); + vact2 = CustomData_get_render_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); } else { vact1 = CustomData_get_active_layer_index(&me->vdata, CD_PROP_COLOR); - vact2 = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPCOL); + vact2 = CustomData_get_active_layer_index(&me->ldata, CD_PROP_BYTE_COLOR); } if (vact1 != -1) { @@ -2689,5 +2731,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings; + if (settings == NULL) { + continue; + } + if (settings->curve_length == 0.0f) { + settings->curve_length = 0.3f; + } + } } } diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index b9491a4913b..0246850123a 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -506,6 +506,17 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_ #define BM_ELEM_CD_GET_INT(ele, offset) \ (BLI_assert(offset != -1), *((int *)((char *)(ele)->head.data + (offset)))) +#define BM_ELEM_CD_SET_BOOL(ele, offset, f) \ + { \ + CHECK_TYPE_NONCONST(ele); \ + BLI_assert(offset != -1); \ + *((bool *)((char *)(ele)->head.data + (offset))) = (f); \ + } \ + (void)0 + +#define BM_ELEM_CD_GET_BOOL(ele, offset) \ + (BLI_assert(offset != -1), *((bool *)((char *)(ele)->head.data + (offset)))) + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) # define BM_ELEM_CD_GET_VOID_P(ele, offset) \ (BLI_assert(offset != -1), \ @@ -530,6 +541,64 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_ #define BM_ELEM_CD_GET_FLOAT(ele, offset) \ (BLI_assert(offset != -1), *((float *)((char *)(ele)->head.data + (offset)))) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + +# define BM_ELEM_CD_GET_FLOAT_P(ele, offset) \ + (BLI_assert(offset != -1), \ + _Generic(ele, \ + GENERIC_TYPE_ANY((float *)POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_NONCONST), \ + GENERIC_TYPE_ANY((const float *)POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_CONST))) + +# define BM_ELEM_CD_GET_FLOAT2_P(ele, offset) \ + (BLI_assert(offset != -1), \ + _Generic(ele, \ + GENERIC_TYPE_ANY((float(*)[2])POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_NONCONST), \ + GENERIC_TYPE_ANY((const float(*)[2])POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_CONST))) + +# define BM_ELEM_CD_GET_FLOAT3_P(ele, offset) \ + (BLI_assert(offset != -1), \ + _Generic(ele, \ + GENERIC_TYPE_ANY((float(*)[3])POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_NONCONST), \ + GENERIC_TYPE_ANY((const float(*)[3])POINTER_OFFSET((ele)->head.data, offset), \ + _BM_GENERIC_TYPE_ELEM_CONST))) + +#else + +# define BM_ELEM_CD_GET_FLOAT_P(ele, offset) \ + (BLI_assert(offset != -1), (float *)((char *)(ele)->head.data + (offset))) + +# define BM_ELEM_CD_GET_FLOAT2_P(ele, offset) \ + (BLI_assert(offset != -1), (float(*)[2])((char *)(ele)->head.data + (offset))) + +# define BM_ELEM_CD_GET_FLOAT3_P(ele, offset) \ + (BLI_assert(offset != -1), (float(*)[3])((char *)(ele)->head.data + (offset))) + +#endif + +#define BM_ELEM_CD_SET_FLOAT2(ele, offset, f) \ + { \ + CHECK_TYPE_NONCONST(ele); \ + BLI_assert(offset != -1); \ + ((float *)((char *)(ele)->head.data + (offset)))[0] = (f)[0]; \ + ((float *)((char *)(ele)->head.data + (offset)))[1] = (f)[1]; \ + } \ + (void)0 + +#define BM_ELEM_CD_SET_FLOAT3(ele, offset, f) \ + { \ + CHECK_TYPE_NONCONST(ele); \ + BLI_assert(offset != -1); \ + ((float *)((char *)(ele)->head.data + (offset)))[0] = (f)[0]; \ + ((float *)((char *)(ele)->head.data + (offset)))[1] = (f)[1]; \ + ((float *)((char *)(ele)->head.data + (offset)))[2] = (f)[2]; \ + } \ + (void)0 + #define BM_ELEM_CD_GET_FLOAT_AS_UCHAR(ele, offset) \ (BLI_assert(offset != -1), (uchar)(BM_ELEM_CD_GET_FLOAT(ele, offset) * 255.0f)) diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c index 35614069f96..aa7f0f511ec 100644 --- a/source/blender/bmesh/operators/bmo_join_triangles.c +++ b/source/blender/bmesh/operators/bmo_join_triangles.c @@ -282,7 +282,7 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op) delimit_data.cdata[delimit_data.cdata_len].cd_offset = -1; if (BMO_slot_bool_get(op->slots_in, "cmp_vcols") && bm_edge_delimit_cdata( - &bm->ldata, CD_MLOOPCOL, &delimit_data.cdata[delimit_data.cdata_len])) { + &bm->ldata, CD_PROP_BYTE_COLOR, &delimit_data.cdata[delimit_data.cdata_len])) { delimit_data.cdata_len += 1; } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index be5521d45ab..8f32f878c58 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -675,7 +675,7 @@ static BMFace *bev_create_ngon(BMesh *bm, const int totv, BMFace **face_arr, BMFace *facerep, - BMEdge **edge_arr, + BMEdge **snap_edge_arr, int mat_nr, bool do_interp) { @@ -699,8 +699,8 @@ static BMFace *bev_create_ngon(BMesh *bm, } if (interp_f) { BMEdge *bme = NULL; - if (edge_arr) { - bme = edge_arr[i]; + if (snap_edge_arr) { + bme = snap_edge_arr[i]; } float save_co[3]; if (bme) { @@ -734,44 +734,6 @@ static BMFace *bev_create_ngon(BMesh *bm, return f; } -static BMFace *bev_create_quad(BMesh *bm, - BMVert *v1, - BMVert *v2, - BMVert *v3, - BMVert *v4, - BMFace *f1, - BMFace *f2, - BMFace *f3, - BMFace *f4, - int mat_nr) -{ - BMVert *varr[4] = {v1, v2, v3, v4}; - BMFace *farr[4] = {f1, f2, f3, f4}; - return bev_create_ngon(bm, varr, 4, farr, f1, NULL, mat_nr, true); -} - -static BMFace *bev_create_quad_ex(BMesh *bm, - BMVert *v1, - BMVert *v2, - BMVert *v3, - BMVert *v4, - BMFace *f1, - BMFace *f2, - BMFace *f3, - BMFace *f4, - BMEdge *e1, - BMEdge *e2, - BMEdge *e3, - BMEdge *e4, - BMFace *frep, - int mat_nr) -{ - BMVert *varr[4] = {v1, v2, v3, v4}; - BMFace *farr[4] = {f1, f2, f3, f4}; - BMEdge *earr[4] = {e1, e2, e3, e4}; - return bev_create_ngon(bm, varr, 4, farr, frep, earr, mat_nr, true); -} - /* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */ static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, int layer_index) { @@ -828,6 +790,25 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f return true; } +/** + * In array face_component of total `totface` elements, swap values c1 and c2 + * wherever they occur. + */ +static void swap_face_components(int *face_component, int totface, int c1, int c2) +{ + if (c1 == c2) { + return; /* Nothing to do. */ + } + for (int f = 0; f < totface; f++) { + if (face_component[f] == c1) { + face_component[f] = c2; + } + else if (face_component[f] == c2) { + face_component[f] = c1; + } + } +} + /* * Set up the fields of bp->math_layer_info. * We always set has_math_layers to the correct value. @@ -836,6 +817,7 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f */ static void math_layer_info_init(BevelParams *bp, BMesh *bm) { + int f; bp->math_layer_info.has_math_layers = false; bp->math_layer_info.face_component = NULL; for (int i = 0; i < bm->ldata.totlayer; i++) { @@ -860,12 +842,12 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm) bool *in_stack = MEM_malloc_arrayN(totface, sizeof(bool), __func__); /* Set all component ids by DFS from faces with unassigned components. */ - for (int f = 0; f < totface; f++) { + for (f = 0; f < totface; f++) { face_component[f] = -1; in_stack[f] = false; } int current_component = -1; - for (int f = 0; f < totface; f++) { + for (f = 0; f < totface; f++) { if (face_component[f] == -1 && !in_stack[f]) { int stack_top = 0; current_component++; @@ -910,6 +892,44 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm) } MEM_freeN(stack); MEM_freeN(in_stack); + /* We can usually get more pleasing result if components 0 and 1 + * are the topmost and bottom-most (in z-coordinate) components, + * so adjust component indices to make that so. */ + if (current_component <= 0) { + return; /* Only one component, so no need to do this. */ + } + BMFace *top_face = NULL; + float top_face_z = -1e30f; + int top_face_component = -1; + BMFace *bot_face = NULL; + float bot_face_z = 1e30f; + int bot_face_component = -1; + for (f = 0; f < totface; f++) { + float cent[3]; + BMFace *bmf = BM_face_at_index(bm, f); + BM_face_calc_center_bounds(bmf, cent); + float fz = cent[2]; + if (fz > top_face_z) { + top_face_z = fz; + top_face = bmf; + top_face_component = face_component[f]; + } + if (fz < bot_face_z) { + bot_face_z = fz; + bot_face = bmf; + bot_face_component = face_component[f]; + } + } + BLI_assert(top_face != NULL && bot_face != NULL); + UNUSED_VARS_NDEBUG(top_face, bot_face); + swap_face_components(face_component, totface, face_component[0], top_face_component); + if (bot_face_component != top_face_component) { + if (bot_face_component == 0) { + /* It was swapped with old top_face_component. */ + bot_face_component = top_face_component; + } + swap_face_components(face_component, totface, face_component[1], bot_face_component); + } } /** @@ -3843,8 +3863,8 @@ static VMesh *new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert * * where ns2 = floor(nseg / 2). * But these overlap data from previous and next i: there are some forced equivalences. * Let's call these indices the canonical ones: we will just calculate data for these - * 0 <= j <= ns2, 0 <= k < ns2 (for odd ns2) - * 0 <= j < ns2, 0 <= k <= ns2 (for even ns2) + * 0 <= j <= ns2, 0 <= k <= ns2 (for odd ns) + * 0 <= j < ns2, 0 <= k <= ns2 (for even ns) * also (j=ns2, k=ns2) at i=0 (for even ns2) * This function returns the canonical one for any i, j, k in [0,n],[0,ns],[0,ns]. */ @@ -4799,46 +4819,85 @@ static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2) return e2; } -/* Snap co to the closest edge of face f. Return the edge in *r_snap_e, - * the coordinates of snap point in r_ snap_co, - * and the distance squared to the snap point as function return */ -static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, float *r_snap_co) +/** + * Find which BoundVerts of \a bv are internal to face \a f. + * That is, when both the face and the point are projected to 2d, + * the point is on the boundary of or inside the projected face. + * There can only be up to three of then, since, including miters, + * that is the maximum number of BoundVerts that can be between two edges. + * Return the number of face-internal vertices found. + */ +static int find_face_internal_boundverts(const BevVert *bv, + const BMFace *f, + BoundVert *(r_internal[3])) { - BMEdge *beste = NULL; - float beste_d2 = 1e20f; - BMIter iter; - BMEdge *e; - BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) { - float closest[3]; - closest_to_line_segment_v3(closest, co, e->v1->co, e->v2->co); - float d2 = len_squared_v3v3(closest, co); - if (d2 < beste_d2) { - beste_d2 = d2; - beste = e; - copy_v3_v3(r_snap_co, closest); + if (f == NULL) { + return 0; + } + int n_internal = 0; + VMesh *vm = bv->vmesh; + BLI_assert(vm != NULL); + BoundVert *v = vm->boundstart; + do { + /* Possible speedup: do the matrix projection done by the following + * once, outside the loop, or even better, cache it if ever done + * in the course of Bevel. */ + if (BM_face_point_inside_test(f, v->nv.co)) { + r_internal[n_internal++] = v; + if (n_internal == 3) { + break; + } } + } while ((v = v->next) != vm->boundstart); + for (int i = n_internal; i < 3; i++) { + r_internal[i] = NULL; } - *r_snap_e = beste; - return beste_d2; + return n_internal; } -/* What would be the area of the polygon around bv if interpolated in face frep? +/** + * Find where the coordinates of the BndVerts in \a bv should snap to in face \a f. + * Face \a f should contain vertex `bv->v`. + * Project the snapped verts to 2d, then return the area of the resulting polygon. + * Usually one BndVert is inside the face, sometimes up to 3 (if there are miters), + * so don't snap those to an edge; all the rest snap to one of the edges of \a bmf + * incident on `bv->v`. */ -static float interp_poly_area(BevVert *bv, BMFace *frep) +static float projected_boundary_area(BevVert *bv, BMFace *f) { + BMEdge *e1, *e2; VMesh *vm = bv->vmesh; - + float(*proj_co)[2] = BLI_array_alloca(proj_co, vm->count); + float axis_mat[3][3]; + axis_dominant_v3_to_m3(axis_mat, f->no); + get_incident_edges(f, bv->v, &e1, &e2); + BLI_assert(e1 != NULL && e2 != NULL); BLI_assert(vm != NULL); - float(*uv_co)[3] = BLI_array_alloca(uv_co, vm->count); BoundVert *v = vm->boundstart; - int n = 0; + int i = 0; + BoundVert *unsnapped[3]; + find_face_internal_boundverts(bv, f, unsnapped); do { - BLI_assert(n < vm->count); - BMEdge *snape; - snap_face_dist_squared(v->nv.v->co, frep, &snape, uv_co[n]); - n++; + float *co = v->nv.v->co; + if (v == unsnapped[0] || v == unsnapped[1] || v == unsnapped[2]) { + mul_v2_m3v3(proj_co[i], axis_mat, co); + } + else { + float snap1[3], snap2[3]; + closest_to_line_segment_v3(snap1, co, e1->v1->co, e1->v2->co); + closest_to_line_segment_v3(snap2, co, e2->v1->co, e2->v2->co); + float d1_sq = len_squared_v3v3(snap1, co); + float d2_sq = len_squared_v3v3(snap2, co); + if (d1_sq <= d2_sq) { + mul_v2_m3v3(proj_co[i], axis_mat, snap1); + } + else { + mul_v2_m3v3(proj_co[i], axis_mat, snap2); + } + } + ++i; } while ((v = v->next) != vm->boundstart); - float area = fabsf(area_poly_v3(uv_co, n)); + float area = area_poly_v2(proj_co, vm->count); return area; } @@ -4851,7 +4910,8 @@ static float interp_poly_area(BevVert *bv, BMFace *frep) */ static bool is_bad_uv_poly(BevVert *bv, BMFace *frep) { - float area = interp_poly_area(bv, frep); + BLI_assert(bv->vmesh != NULL); + float area = projected_boundary_area(bv, frep); return area < BEVEL_EPSILON_BIG; } @@ -4876,6 +4936,8 @@ static BMFace *frep_for_center_poly(BevelParams *bp, BevVert *bv) bool consider_all_faces = bv->selcount == 1; /* Make an array that can hold maximum possible number of choices. */ BMFace **fchoices = BLI_array_alloca(fchoices, bv->edgecount); + /* For each choice, need to remember the unsnapped BoundVerts. */ + for (int i = 0; i < bv->edgecount; i++) { if (!bv->edges[i].is_bev && !consider_all_faces) { continue; @@ -4924,9 +4986,11 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n int ns2 = vm->seg / 2; BMFace *frep; BMEdge *frep_e1, *frep_e2; + BoundVert *frep_unsnapped[3]; if (bv->any_seam) { frep = frep_for_center_poly(bp, bv); get_incident_edges(frep, bv->v, &frep_e1, &frep_e2); + find_face_internal_boundverts(bv, frep, frep_unsnapped); } else { frep = NULL; @@ -4938,8 +5002,13 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v); if (frep) { BLI_array_append(vf, frep); - BMEdge *frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2); - BLI_array_append(ve, v == vm->boundstart ? NULL : frep_e); + if (v == frep_unsnapped[0] || v == frep_unsnapped[1] || v == frep_unsnapped[2]) { + BLI_array_append(ve, NULL); + } + else { + BMEdge *frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2); + BLI_array_append(ve, frep_e); + } } else { BLI_array_append(vf, boundvert_rep_face(v, NULL)); @@ -5242,6 +5311,110 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv) return vm; } +static BMEdge *snap_edge_for_center_vmesh_vert(int i, + int n_bndv, + BMEdge *eprev, + BMEdge *enext, + BMFace **bndv_rep_faces, + BMFace *center_frep, + const bool *frep_beats_next) +{ + int previ = (i + n_bndv - 1) % n_bndv; + int nexti = (i + 1) % n_bndv; + + if (frep_beats_next[previ] && bndv_rep_faces[previ] == center_frep) { + return eprev; + } + if (!frep_beats_next[i] && bndv_rep_faces[nexti] == center_frep) { + return enext; + } + /* If n_bndv > 3 then we won't snap in the boundvert regions + * that are not directly adjacent to the center-winning boundvert. + * This is probably wrong, maybe getting UV positions outside the + * original area, but the alternative may be even worse. */ + return NULL; +} + +/** + * Fill the r_snap_edges array with the edges to snap to (or NUL, if no snapping) + * for the adj mesh face with lower left corner at (i, ring j, segment k). + * The indices of the four corners are (i,j,k), (i,j,k+1), (i,j+1,k+1), (i,j+1,k). + * The answer will be one of NULL (don't snap), eprev (the edge between + * boundvert i and boundvert i-1), or enext (the edge between boundvert i + * and boundvert i+1). + * When n is odd, the center column (seg ns2) is ambiguous as to whether it + * interpolates in the current boundvert's frep [= interpolation face] or the next one's. + * Similarly, when n is odd, the center row (ring ns2) is ambiguous as to + * whether it interpolates in the current boundvert's frep or the previous one's. + * Parameter frep_beats_next should have an array of size n_bndv of bools + * that say whether the tie should be broken in favor of the next boundvert's + * frep (if true) or the current one's. + * For vertices in the center polygon (when ns is odd), the snapping edge depends + * on where the boundvert is in relation to the boundvert that has the center face's frep, + * 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. + */ +static void snap_edges_for_vmesh_vert(int i, + int j, + int k, + int ns, + int ns2, + int n_bndv, + BMEdge *eprev, + BMEdge *enext, + BMEdge *enextnext, + BMFace **bndv_rep_faces, + BMFace *center_frep, + const bool *frep_beats_next, + BMEdge *r_snap_edges[4]) +{ + BLI_assert(0 <= i && i < n_bndv && 0 <= j && j < ns2 && 0 <= k && k <= ns2); + for (int corner = 0; corner < 4; corner++) { + r_snap_edges[corner] = NULL; + if (ns % 2 == 0) { + continue; + } + int previ = (i + n_bndv - 1) % n_bndv; + /* Make jj and kk be the j and k indices for this corner. */ + int jj = corner < 2 ? j : j + 1; + int kk = (corner == 0 || corner == 3) ? k : k + 1; + if (jj < ns2 && kk < ns2) { + ; /* No snap. */ + } + else if (jj < ns2 && kk == ns2) { + /* On the left side of the center strip quads, but not on center poly. */ + if (!frep_beats_next[i]) { + r_snap_edges[corner] = enext; + } + } + else if (jj < ns2 && kk == ns2 + 1) { + /* On the right side of the center strip quads, but not on center poly. */ + if (frep_beats_next[i]) { + r_snap_edges[corner] = enext; + } + } + else if (jj == ns2 && kk < ns2) { + /* On the top of the top strip quads, but not on center poly. */ + if (frep_beats_next[previ]) { + r_snap_edges[corner] = eprev; + } + } + else if (jj == ns2 && kk == ns2) { + /* Center poly vert for boundvert i. */ + r_snap_edges[corner] = snap_edge_for_center_vmesh_vert( + i, n_bndv, eprev, enext, bndv_rep_faces, center_frep, frep_beats_next); + } + else if (jj == ns2 && kk == ns2 + 1) { + /* Center poly vert for boundvert i+1. */ + int nexti = (i + 1) % n_bndv; + r_snap_edges[corner] = snap_edge_for_center_vmesh_vert( + nexti, n_bndv, enext, enextnext, bndv_rep_faces, center_frep, frep_beats_next); + } + } +} + /** * Given that the boundary is built and the boundary #BMVert's have been made, * calculate the positions of the interior mesh points for the M_ADJ pattern, @@ -5295,17 +5468,62 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert } } vmesh_copy_equiv_verts(vm); - /* Make the polygons. */ + + /* Find and store the interpolation face for each BoundVert. */ + BMFace **bndv_rep_faces = BLI_array_alloca(bndv_rep_faces, n_bndv); BoundVert *bndv = vm->boundstart; do { int i = bndv->index; - BMFace *f = boundvert_rep_face(bndv, NULL); - BMFace *f2 = boundvert_rep_face(bndv->next, NULL); - BMFace *fchoices[2] = {f, f2}; - BMFace *fc = odd ? choose_rep_face(bp, fchoices, 2) : NULL; + bndv_rep_faces[i] = boundvert_rep_face(bndv, NULL); + } while ((bndv = bndv->next) != vm->boundstart); - EdgeHalf *e = (bp->affect_type == BEVEL_AFFECT_VERTICES) ? bndv->efirst : bndv->ebev; + /* If odd number of segments, need data to break interpolation ties. */ + BMVert **center_verts = NULL; + BMEdge **center_edge_snaps = NULL; + BMFace **center_face_interps = NULL; + bool *frep_beats_next = NULL; + BMFace *center_frep = NULL; + if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) { + center_verts = BLI_array_alloca(center_verts, n_bndv); + center_edge_snaps = BLI_array_alloca(center_edge_snaps, n_bndv); + center_face_interps = BLI_array_alloca(center_face_interps, n_bndv); + frep_beats_next = BLI_array_alloca(frep_beats_next, n_bndv); + center_frep = frep_for_center_poly(bp, bv); + for (int i = 0; i < n_bndv; i++) { + center_edge_snaps[i] = NULL; + /* frep_beats_next[i] == true if frep for i is chosen over that for i + 1. */ + int inext = (i + 1) % n_bndv; + BMFace *fchoices[2] = {bndv_rep_faces[i], bndv_rep_faces[inext]}; + BMFace *fwinner = choose_rep_face(bp, fchoices, 2); + frep_beats_next[i] = fwinner == bndv_rep_faces[i]; + } + } + /* Make the polygons. */ + bndv = vm->boundstart; + do { + int i = bndv->index; + int inext = bndv->next->index; + BMFace *f = bndv_rep_faces[i]; + BMFace *f2 = bndv_rep_faces[inext]; + BMFace *fc = NULL; + if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) { + fc = frep_beats_next[i] ? f : f2; + } + + EdgeHalf *e, *eprev, *enext; + if (bp->affect_type == BEVEL_AFFECT_VERTICES) { + e = bndv->efirst; + eprev = bndv->prev->efirst; + enext = bndv->next->efirst; + } + else { + e = bndv->ebev; + eprev = bndv->prev->ebev; + enext = bndv->next->ebev; + } BMEdge *bme = e ? e->e : NULL; + BMEdge *bmeprev = eprev ? eprev->e : NULL; + BMEdge *bmenext = enext ? enext->e : NULL; /* For odd ns, make polys with lower left corner at (i,j,k) for * j in [0, ns2-1], k in [0, ns2]. And then the center ngon. * For even ns, @@ -5315,77 +5533,84 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert */ for (int j = 0; j < ns2; j++) { for (int k = 0; k < ns2 + odd; k++) { + /* We will create a quad with these four corners. */ BMVert *bmv1 = mesh_vert(vm, i, j, k)->v; BMVert *bmv2 = mesh_vert(vm, i, j, k + 1)->v; BMVert *bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v; BMVert *bmv4 = mesh_vert(vm, i, j + 1, k)->v; + BMVert *bmvs[4] = {bmv1, bmv2, bmv3, bmv4}; BLI_assert(bmv1 && bmv2 && bmv3 && bmv4); - BMFace *r_f; + /* For each created quad, the UV's etc. will be interpolated + * in potentially a different face for each corner and may need + * to snap to a particular edge before interpolating. + * The fr and se arrays will be filled with the interpolation faces + * and snapping edges for the for corners in the order given + * in the bmvs array. + */ + BMFace *fr[4]; + BMEdge *se[4] = {NULL, NULL, NULL, NULL}; if (bp->affect_type == BEVEL_AFFECT_VERTICES) { + fr[0] = fr[1] = fr[2] = fr[3] = f2; if (j < k) { if (k == ns2 && j == ns2 - 1) { - r_f = bev_create_quad_ex(bm, - bmv1, - bmv2, - bmv3, - bmv4, - f2, - f2, - f2, - f2, - NULL, - NULL, - bndv->next->efirst->e, - bme, - f2, - mat_nr); - } - else { - r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr); + se[2] = bndv->next->efirst->e; + se[3] = bme; } } - else if (j > k) { - r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr); - } - else { /* j == k */ + else if (j == k) { /* Only one edge attached to v, since vertex only. */ - if (e->is_seam) { - r_f = bev_create_quad_ex( - bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, bme, NULL, bme, NULL, f2, mat_nr); - } - else { - r_f = bev_create_quad_ex( - bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f, bme, NULL, bme, NULL, f2, mat_nr); + se[0] = se[2] = bme; + if (!e->is_seam) { + fr[3] = f; } } } else { /* Edge bevel. */ + fr[0] = fr[1] = fr[2] = fr[3] = f; if (odd) { + BMEdge *b1 = (eprev && eprev->is_seam) ? bmeprev : NULL; + BMEdge *b2 = (e && e->is_seam) ? bme : NULL; + BMEdge *b3 = (enext && enext->is_seam) ? bmenext : NULL; + snap_edges_for_vmesh_vert(i, + j, + k, + ns, + ns2, + n_bndv, + b1, + b2, + b3, + bndv_rep_faces, + center_frep, + frep_beats_next, + se); if (k == ns2) { - if (e && e->is_seam) { - r_f = bev_create_quad_ex( - bm, bmv1, bmv2, bmv3, bmv4, fc, fc, fc, fc, NULL, bme, bme, NULL, fc, mat_nr); + if (!e || e->is_seam) { + fr[0] = fr[1] = fr[2] = fr[3] = fc; } else { - r_f = bev_create_quad_ex( - bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, NULL, bme, bme, NULL, fc, mat_nr); + fr[0] = fr[3] = f; + fr[1] = fr[2] = f2; + } + if (j == ns2 - 1) { + /* Use the 4th vertex of these faces as the ones used for the center polygon. */ + center_verts[i] = bmvs[3]; + center_edge_snaps[i] = se[3]; + center_face_interps[i] = bv->any_seam ? center_frep : f; } - } - else { - r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr); } } - else { - BMEdge *bme1 = k == ns2 - 1 ? bme : NULL; - BMEdge *bme3 = NULL; + else { /* Edge bevel, Even number of segments. */ + if (k == ns2 - 1) { + se[1] = bme; + } if (j == ns2 - 1 && bndv->prev->ebev) { - bme3 = bndv->prev->ebev->e; + se[3] = bmeprev; } - BMEdge *bme2 = bme1 != NULL ? bme1 : bme3; - r_f = bev_create_quad_ex( - bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, NULL, bme1, bme2, bme3, f, mat_nr); + se[2] = se[1] != NULL ? se[1] : se[3]; } } + BMFace *r_f = bev_create_ngon(bm, bmvs, 4, fr, NULL, se, mat_nr, true); record_face_kind(bp, r_f, F_VERT); } } @@ -5413,7 +5638,18 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert /* Center ngon. */ if (odd) { - build_center_ngon(bp, bm, bv, mat_nr); + if (bp->affect_type == BEVEL_AFFECT_EDGES) { + BMFace *frep = NULL; + if (bv->any_seam) { + frep = frep_for_center_poly(bp, bv); + } + BMFace *cen_f = bev_create_ngon( + bm, center_verts, n_bndv, center_face_interps, frep, center_edge_snaps, mat_nr, true); + record_face_kind(bp, cen_f, F_VERT); + } + else { + build_center_ngon(bp, bm, bv, mat_nr); + } } } @@ -5425,7 +5661,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert * are two problems currently: * - Miter profiles don't have plane_no filled, so down direction is incorrect. * - Indexing profile points of miters with (i, 0, k) seems to return zero except for the first - * and last profile point. + * and last profile point. * TODO(Hans): Use repface / edge arrays for UV interpolation properly. */ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) @@ -5591,9 +5827,11 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv) BMFace *repface; BMEdge *repface_e1, *repface_e2; + BoundVert *unsnapped[3]; if (bv->any_seam) { repface = frep_for_center_poly(bp, bv); get_incident_edges(repface, bv->v, &repface_e1, &repface_e2); + find_face_internal_boundverts(bv, repface, unsnapped); } else { repface = NULL; @@ -5607,8 +5845,13 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv) BLI_array_append(bmverts, bndv->nv.v); if (repface) { BLI_array_append(bmfaces, repface); - BMEdge *frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2); - BLI_array_append(bmedges, n > 0 ? frep_e : NULL); + if (bndv == unsnapped[0] || bndv == unsnapped[1] || bndv == unsnapped[2]) { + BLI_array_append(bmedges, NULL); + } + else { + BMEdge *frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2); + BLI_array_append(bmedges, frep_e); + } } else { BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL)); @@ -6359,10 +6602,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) sub_v3_v3v3(edge_dir, bv->v->co, v2->co); float z = fabsf(2.0f * sinf(angle_v3v3(vert_axis, edge_dir))); if (z < BEVEL_EPSILON) { - e->offset_l_spec = 0.01f * bv->offset; /* Undefined behavior, so tiny bevel. */ + e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */ } else { - e->offset_l_spec = bv->offset / z; + e->offset_l_spec = bp->offset / z; } break; } @@ -6371,10 +6614,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) sub_v3_v3v3(edge_dir, bv->v->co, v2->co); float z = fabsf(cosf(angle_v3v3(vert_axis, edge_dir))); if (z < BEVEL_EPSILON) { - e->offset_l_spec = 0.01f * bv->offset; /* Undefined behavior, so tiny bevel. */ + e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */ } else { - e->offset_l_spec = bv->offset / z; + e->offset_l_spec = bp->offset / z; } break; } @@ -6829,13 +7072,20 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) int odd = nseg % 2; int mid = nseg / 2; BMEdge *center_bme = NULL; + BMFace *fchoices[2] = {f1, f2}; + BMFace *f_choice = NULL; + int center_adj_k = -1; + if (odd & e1->is_seam) { + f_choice = choose_rep_face(bp, fchoices, 2); + if (nseg > 1) { + center_adj_k = f_choice == f1 ? mid + 2 : mid; + } + } for (int k = 1; k <= nseg; k++) { verts[3] = mesh_vert(vm1, i1, 0, k)->v; verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v; BMFace *r_f; if (odd && k == mid + 1) { - BMFace *fchoices[2] = {f1, f2}; - BMFace *f_choice = choose_rep_face(bp, fchoices, 2); if (e1->is_seam) { /* Straddles a seam: choose to interpolate in f_choice and snap the loops whose verts * are in the non-chosen face to bme for interpolation purposes. @@ -6856,6 +7106,25 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) r_f = bev_create_ngon(bm, verts, 4, faces, f_choice, NULL, mat_nr, true); } } + else if (odd && k == center_adj_k && e1->is_seam) { + /* The strip adjacent to the center one, in another UV island. + * Snap the edge near the seam to bme to match what happens in + * the bevel rings. + */ + BMEdge *edges[4]; + BMFace *f_interp; + if (k == mid) { + edges[0] = edges[1] = NULL; + edges[2] = edges[3] = bme; + f_interp = f1; + } + else { + edges[0] = edges[1] = bme; + edges[2] = edges[3] = NULL; + f_interp = f2; + } + r_f = bev_create_ngon(bm, verts, 4, NULL, f_interp, edges, mat_nr, true); + } else if (!odd && k == mid) { /* Left poly that touches an even center line on right. */ BMEdge *edges[4] = {NULL, NULL, bme, bme}; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6d3c203b076..8c54c923476 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -82,6 +82,7 @@ set(SRC intern/draw_cache_impl_volume.c intern/draw_color_management.cc intern/draw_common.c + intern/draw_curves.cc intern/draw_debug.c intern/draw_fluid.c intern/draw_hair.c @@ -98,6 +99,7 @@ set(SRC intern/draw_texture_pool.cc intern/draw_view.c intern/draw_view_data.cc + intern/draw_volume.cc intern/smaa_textures.c engines/basic/basic_engine.c engines/basic/basic_shader.c @@ -194,6 +196,7 @@ set(SRC intern/draw_color_management.h intern/draw_common.h intern/draw_common_shader_shared.h + intern/draw_curves_private.h intern/draw_debug.h intern/draw_hair_private.h intern/draw_instance_data.h diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 81edee17c76..47e8f234a01 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -353,8 +353,6 @@ static void eevee_draw_scene(void *vedata) EEVEE_renderpasses_draw_debug(vedata); - EEVEE_volumes_free_smoke_textures(); - stl->g_data->view_updated = false; DRW_view_set_active(NULL); @@ -574,7 +572,6 @@ static void eevee_render_to_image(void *vedata, } } - EEVEE_volumes_free_smoke_textures(); EEVEE_motion_blur_data_free(&ved->stl->effects->motion_blur); if (RE_engine_test_break(engine)) { diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 2e1b92de068..4f562dd9804 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -965,7 +965,7 @@ static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lb txl->color = NULL; DRW_render_instance_buffer_finish(); - DRW_hair_update(); + DRW_curves_update(); } static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache) @@ -1463,9 +1463,6 @@ void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float } eevee_lightbake_delete_resources(lbake); - - /* Free GPU smoke textures and the smoke domain list correctly: See also T73921. */ - EEVEE_volumes_free_smoke_textures(); } void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index e3342508a14..2e0937dbe49 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -440,9 +440,9 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) DRW_render_instance_buffer_finish(); /* Need to be called after #DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for #DRW_hair_update. */ + /* Also we weed to have a correct FBO bound for #DRW_curves_update. */ GPU_framebuffer_bind(vedata->fbl->main_fb); - DRW_hair_update(); + DRW_curves_update(); DRW_cache_restart(); } diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 9f97dacf9fe..ee336326166 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1558,7 +1558,6 @@ void EEVEE_volumes_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_volumes_resolve(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples); void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_volumes_free_smoke_textures(void); void EEVEE_volumes_free(void); /* eevee_effects.c */ diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 47e2b95f367..bef19c589c2 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -538,9 +538,9 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl DRW_render_instance_buffer_finish(); /* Need to be called after DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct FBO bound for DRW_hair_update */ + /* Also we weed to have a correct FBO bound for DRW_curves_update */ GPU_framebuffer_bind(fbl->main_fb); - DRW_hair_update(); + DRW_curves_update(); /* Sort transparents before the loop. */ DRW_pass_sort_shgroup_z(psl->transparent_pass); diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 4e4a2a9eb8e..85cc7f65126 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -1487,6 +1487,10 @@ struct GPUMaterial *EEVEE_material_get( GPUMaterial *mat = eevee_material_get_ex(scene, ma, wo, options, deferred); int status = GPU_material_status(mat); + /* Return null material and bypass drawing for volume shaders. */ + if ((options & VAR_MAT_VOLUME) && status != GPU_MAT_SUCCESS) { + return NULL; + } switch (status) { case GPU_MAT_SUCCESS: break; diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc index bb1a0b0abe4..05577944140 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc +++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc @@ -81,7 +81,7 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, const bool do_fragment_attrib_load = is_background || is_volume; if (is_hair && !info.vertex_out_interfaces_.is_empty()) { - /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */ + /** Hair attributes come from sampler buffer. Transfer attributes to sampler. */ for (auto &input : info.vertex_inputs_) { info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH); } @@ -97,12 +97,26 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, } attr_load << "};\n"; attr_load << iface.name << " " << iface.instance_name << ";\n"; - /* Global vars just to make code valid. Only Orco is supported. */ - for (const ShaderCreateInfo::VertIn &in : info.vertex_inputs_) { - attr_load << in.type << " " << in.name << ";\n"; + if (!is_volume) { + /* Global vars just to make code valid. Only Orco is supported. */ + for (const ShaderCreateInfo::VertIn &in : info.vertex_inputs_) { + attr_load << in.type << " " << in.name << ";\n"; + } } info.vertex_out_interfaces_.clear(); } + if (is_volume) { + /** Volume grid attributes come from 3D textures. Transfer attributes to samplers. */ + for (auto &input : info.vertex_inputs_) { + info.sampler(0, ImageType::FLOAT_3D, input.name, Frequency::BATCH); + } + info.additional_info("draw_volume_infos"); + /* Do not add twice. */ + if (!GPU_material_flag_get(gpumat, GPU_MATFLAG_OBJECT_INFO)) { + info.additional_info("draw_object_infos"); + } + info.vertex_inputs_.clear(); + } if (!is_volume) { info.define("EEVEE_GENERATED_INTERFACE"); @@ -137,7 +151,7 @@ void eevee_shader_material_create_info_amend(GPUMaterial *gpumat, } frag_gen << "Closure nodetree_exec()\n"; frag_gen << "{\n"; - if (GPU_material_is_volume_shader(gpumat)) { + if (is_volume) { frag_gen << ((codegen.volume) ? codegen.volume : "return CLOSURE_DEFAULT;\n"); } else { diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 6a0c9fb36df..b8bef61f8b1 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -43,35 +43,8 @@ static struct { GPUTexture *dummy_scatter; GPUTexture *dummy_transmit; - - /* List of all fluid simulation / smoke domains rendered within this frame. */ - ListBase smoke_domains; } e_data = {NULL}; /* Engine data */ -static void eevee_create_textures_volumes(void) -{ - const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - e_data.dummy_zero = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, zero); - - const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - e_data.dummy_one = DRW_texture_create_3d(1, 1, 1, GPU_RGBA8, DRW_TEX_WRAP, one); - - const float flame = 0.0f; - e_data.dummy_flame = DRW_texture_create_3d(1, 1, 1, GPU_R8, DRW_TEX_WRAP, &flame); -} - -static GPUTexture *eevee_volume_default_texture(eGPUVolumeDefaultValue default_value) -{ - switch (default_value) { - case GPU_VOLUME_DEFAULT_0: - return e_data.dummy_zero; - case GPU_VOLUME_DEFAULT_1: - return e_data.dummy_one; - } - - return e_data.dummy_zero; -} - void EEVEE_volumes_set_jitter(EEVEE_ViewLayerData *sldata, uint current_sample) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; @@ -227,11 +200,6 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) Scene *scene = draw_ctx->scene; DRWShadingGroup *grp = NULL; - /* Textures */ - if (!e_data.dummy_zero) { - eevee_create_textures_volumes(); - } - /* Quick breakdown of the Volumetric rendering: * * The rendering is separated in 4 stages: @@ -268,7 +236,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) !LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) { struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, NULL, wo, VAR_MAT_VOLUME); - if (GPU_material_has_volume_output(mat)) { + if (mat && GPU_material_has_volume_output(mat)) { grp = DRW_shgroup_material_create(mat, psl->volumetric_world_ps); } @@ -283,11 +251,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); /* Fix principle volumetric not working with world materials. */ - ListBase gpu_grids = GPU_material_volume_grids(mat); - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, &gpu_grids) { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } + grp = DRW_shgroup_volume_create_sub(NULL, NULL, grp, mat); DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); @@ -299,187 +263,17 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* If no world or volume material is present just clear the buffer with this drawcall */ grp = DRW_shgroup_create(EEVEE_shaders_volumes_clear_sh_get(), psl->volumetric_world_ps); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); } } -static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWShadingGroup *grp) -{ - Volume *volume = ob->data; - BKE_volume_load(volume, G.main); - - /* Test if we need to use multiple transforms. */ - DRWVolumeGrid *first_drw_grid = NULL; - bool multiple_transforms = true; - - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name); - DRWVolumeGrid *drw_grid = (volume_grid) ? - DRW_volume_batch_cache_get_grid(volume, volume_grid) : - NULL; - - if (drw_grid) { - if (first_drw_grid == NULL) { - first_drw_grid = drw_grid; - } - else if (drw_grid && - !equals_m4m4(drw_grid->object_to_texture, first_drw_grid->object_to_texture)) { - multiple_transforms = true; - break; - } - } - } - - /* Bail out of no grids to render. */ - if (first_drw_grid == NULL) { - return false; - } - - /* Set transform matrix for the volume as a whole. This one is also used for - * clipping so must map the entire bounding box to 0..1. */ - float bounds_to_object[4][4]; - - if (multiple_transforms) { - /* For multiple grids with different transform, we first transform from object space - * to bounds, then for each individual grid from bounds to texture. */ - const BoundBox *bb = BKE_volume_boundbox_get(ob); - float bb_size[3]; - sub_v3_v3v3(bb_size, bb->vec[6], bb->vec[0]); - size_to_mat4(bounds_to_object, bb_size); - copy_v3_v3(bounds_to_object[3], bb->vec[0]); - - invert_m4_m4(first_drw_grid->object_to_bounds, bounds_to_object); - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_bounds); - } - else { - /* All grid transforms are equal, we can transform to texture space immediately. */ - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", first_drw_grid->object_to_texture); - } - - /* Don't use orco transform here, only matrix. */ - DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoLoc", (float[3]){0.5f, 0.5f, 0.5f}); - DRW_shgroup_uniform_vec3_copy(grp, "volumeOrcoSize", (float[3]){0.5f, 0.5f, 0.5f}); - - /* Set density scale. */ - const float density_scale = BKE_volume_density_scale(volume, ob->obmat); - DRW_shgroup_uniform_float_copy(grp, "volumeDensityScale", density_scale); - - /* Bind volume grid textures. */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name); - DRWVolumeGrid *drw_grid = (volume_grid) ? - DRW_volume_batch_cache_get_grid(volume, volume_grid) : - NULL; - - /* Handle 3 cases here: - * - Grid exists and texture was loaded -> use texture. - * - Grid exists but has zero size or failed to load -> use zero. - * - Grid does not exist -> use default value. */ - GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture : - (volume_grid) ? e_data.dummy_zero : - eevee_volume_default_texture(gpu_grid->default_value); - - DRW_shgroup_uniform_texture(grp, gpu_grid->sampler_name, grid_tex); - - if (drw_grid && multiple_transforms) { - /* Specify per-volume transform matrix that is applied after the - * transform from object to bounds. */ - mul_m4_m4m4(drw_grid->bounds_to_texture, drw_grid->object_to_texture, bounds_to_object); - DRW_shgroup_uniform_mat4(grp, gpu_grid->transform_name, drw_grid->bounds_to_texture); - } - } - - return true; -} - -static bool eevee_volume_object_mesh_init(Scene *scene, - Object *ob, - ListBase *gpu_grids, - DRWShadingGroup *grp) -{ - static const float white[3] = {1.0f, 1.0f, 1.0f}; - ModifierData *md = NULL; - - /* Smoke Simulation */ - if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) && - (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) && - ((FluidModifierData *)md)->domain != NULL) { - FluidModifierData *fmd = (FluidModifierData *)md; - FluidDomainSettings *fds = fmd->domain; - - /* Don't try to show liquid domains here. */ - if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) { - return false; - } - - /* Don't show smoke before simulation starts, this could be made an option in the future. */ - /* (sebbas): Always show smoke for manta */ -#if 0 - const DRWContextState *draw_ctx = DRW_context_state_get(); - const bool show_smoke = ((int)DEG_get_ctime(draw_ctx->depsgraph) >= - *fds->point_cache[0]->startframe); -#endif - - if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS) /* && show_smoke */) { - DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); - BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(fmd)); - } - - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - if (STREQ(gpu_grid->name, "density")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_density ? &fds->tex_density : &e_data.dummy_one); - } - else if (STREQ(gpu_grid->name, "color")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_color ? &fds->tex_color : &e_data.dummy_one); - } - else if (STR_ELEM(gpu_grid->name, "flame", "temperature")) { - DRW_shgroup_uniform_texture_ref( - grp, gpu_grid->sampler_name, fds->tex_flame ? &fds->tex_flame : &e_data.dummy_flame); - } - else { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } - } - - /* Constant Volume color. */ - bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 && - (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0); - - DRW_shgroup_uniform_vec3( - grp, "volumeColor", (use_constant_color) ? fds->active_color : white, 1); - - /* Output is such that 0..1 maps to 0..1000K */ - DRW_shgroup_uniform_vec2(grp, "volumeTemperature", &fds->flame_ignition, 1); - } - else { - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) { - DRW_shgroup_uniform_texture( - grp, gpu_grid->sampler_name, eevee_volume_default_texture(gpu_grid->default_value)); - } - } - - /* Transform for mesh volumes. */ - static const float unit_mat[4][4] = {{1.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 1.0f}}; - float *texco_loc, *texco_size; - BKE_mesh_texspace_get_reference((struct Mesh *)ob->data, NULL, &texco_loc, &texco_size); - - DRW_shgroup_uniform_mat4(grp, "volumeObjectToTexture", unit_mat); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoLoc", texco_loc, 1); - DRW_shgroup_uniform_vec3(grp, "volumeOrcoSize", texco_size, 1); - - return true; -} - void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Scene *scene, @@ -506,15 +300,22 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, int mat_options = VAR_MAT_VOLUME | VAR_MAT_MESH; struct GPUMaterial *mat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options); - eGPUMaterialStatus status = GPU_material_status(mat); /* If shader failed to compile or is currently compiling. */ - if (status != GPU_MAT_SUCCESS) { + if (mat == NULL) { return; } + /* TODO(fclem): Reuse main shading group to avoid shading binding cost just like for surface + * shaders. */ DRWShadingGroup *grp = DRW_shgroup_material_create(mat, vedata->psl->volumetric_objects_ps); + grp = DRW_shgroup_volume_create_sub(scene, ob, grp, mat); + + if (grp == NULL) { + return; + } + /* TODO(fclem): remove those "unnecessary" UBOs */ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); @@ -522,22 +323,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); - - ListBase gpu_grids = GPU_material_volume_grids(mat); - - if (ob->type == OB_VOLUME) { - if (!eevee_volume_object_grids_init(ob, &gpu_grids, grp)) { - return; - } - } - else { - if (!eevee_volume_object_mesh_init(scene, ob, &gpu_grids, grp)) { - return; - } - } - /* TODO: Reduce to number of slices intersecting. */ /* TODO: Preemptive culling. */ DRW_shgroup_call_procedural_triangles(grp, ob, sldata->common_data.vol_tex_size[2]); @@ -753,16 +539,6 @@ void EEVEE_volumes_resolve(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *veda } } -void EEVEE_volumes_free_smoke_textures(void) -{ - /* Free Smoke Textures after rendering */ - LISTBASE_FOREACH (LinkData *, link, &e_data.smoke_domains) { - FluidModifierData *fmd = (FluidModifierData *)link->data; - DRW_smoke_free(fmd); - } - BLI_freelistN(&e_data.smoke_domains); -} - void EEVEE_volumes_free(void) { DRW_TEXTURE_FREE_SAFE(e_data.dummy_scatter); diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl index 0e8e8dd9d01..ab0f4d6bec8 100644 --- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl @@ -144,3 +144,13 @@ vec3 attr_load_uv(vec3 attr) return attr; } #endif + +/* Passthrough. */ +float attr_load_temperature_post(float attr) +{ + return attr; +} +vec4 attr_load_color_post(vec4 attr) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl index 9ad7a4fdbc1..79ec3807d0b 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl @@ -179,3 +179,13 @@ vec3 attr_load_uv(vec3 attr) { return vec3(0); } + +/* Passthrough. */ +float attr_load_temperature_post(float attr) +{ + return attr; +} +vec4 attr_load_color_post(vec4 attr) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl index 6c6b810422b..49c18832f72 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl @@ -157,3 +157,13 @@ vec3 attr_load_uv(vec3 attr) return attr; } #endif + +/* Passthrough. */ +float attr_load_temperature_post(float attr) +{ + return attr; +} +vec4 attr_load_color_post(vec4 attr) +{ + return attr; +} diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl index e0a79872928..914261d7f59 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl @@ -4,17 +4,13 @@ /* Based on Frosbite Unified Volumetric. * https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */ -#ifdef MESH_SHADER -uniform vec3 volumeOrcoLoc; -uniform vec3 volumeOrcoSize; -uniform mat4 volumeObjectToTexture; -uniform float volumeDensityScale = 1.0; -#endif +/* Store volumetric properties into the froxel textures. */ flat in int slice; /* Warning: these are not attributes, these are global vars. */ vec3 worldPosition = vec3(0.0); +vec3 objectPosition = vec3(0.0); vec3 viewPosition = vec3(0.0); vec3 viewNormal = vec3(0.0); vec3 volumeOrco = vec3(0.0); @@ -24,9 +20,9 @@ layout(location = 1) out vec4 volumeExtinction; layout(location = 2) out vec4 volumeEmissive; layout(location = 3) out vec4 volumePhase; -/* Store volumetric properties into the froxel textures. */ +int attr_id; -#ifdef MESH_SHADER +#ifndef CLEAR GlobalData init_globals(void) { GlobalData surf; @@ -80,10 +76,8 @@ void main() viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z); worldPosition = point_view_to_world(viewPosition); #ifdef MESH_SHADER - volumeOrco = point_world_to_object(worldPosition); - /* TODO: redundant transform */ - volumeOrco = (volumeOrco - volumeOrcoLoc + volumeOrcoSize) / (volumeOrcoSize * 2.0); - volumeOrco = (volumeObjectToTexture * vec4(volumeOrco, 1.0)).xyz; + objectPosition = point_world_to_object(worldPosition); + 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. @@ -100,15 +94,13 @@ void main() volumeEmissive = vec4(0.0, 0.0, 0.0, 1.0); volumePhase = vec4(0.0, 0.0, 0.0, 0.0); #else -# ifdef MESH_SHADER g_data = init_globals(); attrib_load(); -# endif Closure cl = nodetree_exec(); # ifdef MESH_SHADER - cl.scatter *= volumeDensityScale; - cl.absorption *= volumeDensityScale; - cl.emission *= volumeDensityScale; + cl.scatter *= drw_volume.density_scale; + cl.absorption *= drw_volume.density_scale; + cl.emission *= drw_volume.density_scale; # endif volumeScattering = vec4(cl.scatter, 1.0); @@ -124,35 +116,72 @@ void main() #endif } -vec3 attr_load_orco(vec4 orco) +vec3 grid_coordinates() +{ + vec3 co = volumeOrco; +#ifdef MESH_SHADER + /* Optional per-grid transform. */ + if (drw_volume.grids_xform[attr_id][3][3] != 0.0) { + co = (drw_volume.grids_xform[attr_id] * vec4(objectPosition, 1.0)).xyz; + } +#endif + attr_id += 1; + return co; +} + +vec3 attr_load_orco(sampler3D orco) { + attr_id += 1; return volumeOrco; } -vec4 attr_load_tangent(vec4 tangent) +vec4 attr_load_tangent(sampler3D tangent) { + attr_id += 1; return vec4(0); } -vec4 attr_load_vec4(vec4 attr) +vec4 attr_load_vec4(sampler3D tex) { - return vec4(0); + return texture(tex, grid_coordinates()); } -vec3 attr_load_vec3(vec3 attr) +vec3 attr_load_vec3(sampler3D tex) { - return vec3(0); + return texture(tex, grid_coordinates()).rgb; } -vec2 attr_load_vec2(vec2 attr) +vec2 attr_load_vec2(sampler3D tex) { - return vec2(0); + return texture(tex, grid_coordinates()).rg; } -float attr_load_float(float attr) +float attr_load_float(sampler3D tex) { - return 0.0; + return texture(tex, grid_coordinates()).r; } -vec4 attr_load_color(vec4 attr) +vec4 attr_load_color(sampler3D tex) { - return vec4(0); + return texture(tex, grid_coordinates()); } -vec3 attr_load_uv(vec3 attr) +vec3 attr_load_uv(sampler3D attr) { + attr_id += 1; return vec3(0); } + +/* 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. */ +float attr_load_temperature_post(float attr) +{ +#ifdef MESH_SHADER + /* Bring the into standard range without having to modify the grid values */ + attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0; +#endif + return attr; +} +vec4 attr_load_color_post(vec4 attr) +{ +#ifdef MESH_SHADER + /* Density is premultiplied for interpolation, divide it out here. */ + attr.rgb *= safe_rcp(attr.a); + attr.rgb *= drw_volume.color_mul.rgb; + attr.a = 1.0; +#endif + return attr; +} 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 26b60c992e1..527bbd18896 100644 --- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl @@ -11,10 +11,8 @@ uniform sampler3D volumeScattering; /* Result of the scatter step */ uniform sampler3D volumeExtinction; #ifdef USE_VOLUME_OPTI -uniform layout(r11f_g11f_b10f) -writeonly restrict image3D finalScattering_img; -uniform layout(r11f_g11f_b10f) -writeonly restrict image3D finalTransmittance_img; +uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalScattering_img; +uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img; vec3 finalScattering; vec3 finalTransmittance; diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index abcca5525c7..5d8ba06e181 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -1474,23 +1474,6 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, } if (draw_velocity || show_gridlines) { - BLI_addtail(&data->stl->pd->smoke_domains, BLI_genericNodeN(fmd)); - } -} - -static void OVERLAY_volume_free_smoke_textures(OVERLAY_Data *data) -{ - /* Free Smoke Textures after rendering */ - /* XXX This is a waste of processing and GPU bandwidth if nothing - * is updated. But the problem is since Textures are stored in the - * modifier we don't want them to take precious VRAM if the - * modifier is not used for display. We should share them for - * all viewport in a redraw at least. */ - LinkData *link; - while ((link = BLI_pophead(&data->stl->pd->smoke_domains))) { - FluidModifierData *fmd = (FluidModifierData *)link->data; - DRW_smoke_free_velocity(fmd); - MEM_freeN(link); } } @@ -1624,8 +1607,6 @@ void OVERLAY_extra_draw(OVERLAY_Data *vedata) void OVERLAY_extra_in_front_draw(OVERLAY_Data *vedata) { DRW_draw_pass(vedata->psl->extra_ps[1]); - - OVERLAY_volume_free_smoke_textures(vedata); } void OVERLAY_extra_centers_draw(OVERLAY_Data *vedata) diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 71b6c9424e6..4c933e08a57 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -304,7 +304,6 @@ typedef struct OVERLAY_PrivateData { DRWView *view_edit_curves_points; /** TODO: get rid of this. */ - ListBase smoke_domains; ListBase bg_movie_clips; /** Two instances for in_front option and without. */ diff --git a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh index 5d098d61dbf..698c7d1a8b7 100644 --- a/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh +++ b/source/blender/draw/engines/workbench/shaders/infos/workbench_volume_info.hh @@ -55,7 +55,6 @@ GPU_SHADER_CREATE_INFO(workbench_volume_coba) GPU_SHADER_CREATE_INFO(workbench_volume_no_coba) .sampler(4, ImageType::FLOAT_3D, "shadowTexture") - .sampler(5, ImageType::UINT_2D, "transferTexture") .push_constant(Type::VEC3, "activeColor"); /** \} */ diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index f4e042933d1..f7f156e5297 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -163,7 +163,6 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd) wpd->taa_sample_len = workbench_antialiasing_sample_count_get(wpd); wpd->volumes_do = false; - BLI_listbase_clear(&wpd->smoke_domains); /* FIXME: This reproduce old behavior when workbench was separated in 2 engines. * But this is a workaround for a missing update tagging. */ diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 566fd30096d..fb20bde2f65 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -271,6 +271,18 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, BKE_pbvh_is_drawing_set(ob->sculpt->pbvh, is_sculpt_pbvh); } + bool has_color = false; + + if (me) { + const CustomData *cd_vdata = workbench_mesh_get_vert_custom_data(me); + const CustomData *cd_ldata = workbench_mesh_get_loop_custom_data(me); + + has_color = (CustomData_has_layer(cd_vdata, CD_PROP_COLOR) || + CustomData_has_layer(cd_vdata, CD_PROP_BYTE_COLOR) || + CustomData_has_layer(cd_ldata, CD_PROP_COLOR) || + CustomData_has_layer(cd_ldata, CD_PROP_BYTE_COLOR)); + } + if (color_type == V3D_SHADING_TEXTURE_COLOR) { if (ob->dt < OB_TEXTURE) { color_type = V3D_SHADING_MATERIAL_COLOR; @@ -285,14 +297,6 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, color_type = V3D_SHADING_OBJECT_COLOR; } else { - const CustomData *cd_vdata = workbench_mesh_get_vert_custom_data(me); - const CustomData *cd_ldata = workbench_mesh_get_loop_custom_data(me); - - bool has_color = (CustomData_has_layer(cd_vdata, CD_PROP_COLOR) || - CustomData_has_layer(cd_vdata, CD_MLOOPCOL) || - CustomData_has_layer(cd_ldata, CD_PROP_COLOR) || - CustomData_has_layer(cd_ldata, CD_MLOOPCOL)); - if (!has_color) { color_type = V3D_SHADING_OBJECT_COLOR; } @@ -314,7 +318,7 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, *r_texpaint_mode = true; } } - else if (is_vertpaint_mode && me && CustomData_has_layer(ldata, CD_MLOOPCOL)) { + else if (is_vertpaint_mode && me && has_color) { color_type = V3D_SHADING_VERTEX_COLOR; } } @@ -613,10 +617,8 @@ static void workbench_draw_scene(void *ved) workbench_draw_finish(vedata); } -void workbench_draw_finish(void *ved) +void workbench_draw_finish(void *UNUSED(ved)) { - WORKBENCH_Data *vedata = ved; - workbench_volume_draw_finish(vedata); /* Reset default view. */ DRW_view_set_active(NULL); } diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 727b771ee08..492bce1e571 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -324,10 +324,6 @@ typedef struct WORKBENCH_PrivateData { /** Index of current material inside the material chunk. Only for material coloring mode. */ int material_index; - /* Volumes */ - /** List of smoke domain textures to free after drawing. */ - ListBase smoke_domains; - /* Depth of Field */ /** Depth of field temp buffers. */ struct GPUTexture *dof_blur_tx; @@ -533,7 +529,6 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, struct ModifierData *md, eV3DShadingColorType color_type); void workbench_volume_draw_pass(WORKBENCH_Data *vedata); -void workbench_volume_draw_finish(WORKBENCH_Data *vedata); /* workbench_engine.c */ diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 72de3fe298a..1279682e899 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -170,9 +170,9 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct FBO bound for #DRW_hair_update */ + /* Also we weed to have a correct FBO bound for #DRW_curves_update */ GPU_framebuffer_bind(dfbl->default_fb); - DRW_hair_update(); + DRW_curves_update(); GPU_framebuffer_bind(dfbl->default_fb); GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index c6f40c5d6bb..2c902e9b627 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -178,8 +178,6 @@ static void workbench_volume_modifier_cache_populate(WORKBENCH_Data *vedata, else { DRW_shgroup_call(grp, DRW_cache_cube_get(), ob); } - - BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(fmd)); } static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd, @@ -334,20 +332,3 @@ void workbench_volume_draw_pass(WORKBENCH_Data *vedata) DRW_draw_pass(psl->volume_ps); } } - -void workbench_volume_draw_finish(WORKBENCH_Data *vedata) -{ - WORKBENCH_PrivateData *wpd = vedata->stl->wpd; - - /* Free Smoke Textures after rendering */ - /* XXX This is a waste of processing and GPU bandwidth if nothing - * is updated. But the problem is since Textures are stored in the - * modifier we don't want them to take precious VRAM if the - * modifier is not used for display. We should share them for - * all viewport in a redraw at least. */ - LISTBASE_FOREACH (LinkData *, link, &wpd->smoke_domains) { - FluidModifierData *fmd = (FluidModifierData *)link->data; - DRW_smoke_free(fmd); - } - BLI_freelistN(&wpd->smoke_domains); -} diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index ed94c485b32..d7e752a43f4 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -53,6 +53,8 @@ * */ +#include "DRW_render.h" + #include "MEM_guardedalloc.h" #include "draw_texture_pool.h" @@ -61,6 +63,7 @@ #include "BLI_span.hh" #include "BLI_utildefines.h" #include "BLI_utility_mixins.hh" +#include "BLI_vector.hh" #include "GPU_framebuffer.h" #include "GPU_storage_buffer.h" diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index b084a5e5945..f2742f3bcc7 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -29,9 +29,11 @@ #include "GPU_material.h" #include "GPU_texture.h" +#include "DRW_render.h" + #include "draw_cache_impl.h" /* own include */ #include "draw_cache_inline.h" -#include "draw_hair_private.h" /* own include */ +#include "draw_curves_private.h" /* own include */ using blender::float3; using blender::IndexRange; @@ -41,7 +43,7 @@ using blender::Span; /* Curves GPUBatch Cache */ struct CurvesBatchCache { - ParticleHairCache hair; + CurvesEvalCache curves_cache; GPUBatch *edit_points; @@ -70,6 +72,28 @@ static void curves_batch_cache_init(Curves &curves) cache->is_dirty = false; } +static void curves_batch_cache_clear_data(CurvesEvalCache &curves_cache) +{ + /* TODO: more granular update tagging. */ + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_point_buf); + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_length_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.point_tex); + DRW_TEXTURE_FREE_SAFE(curves_cache.length_tex); + + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_strand_buf); + GPU_VERTBUF_DISCARD_SAFE(curves_cache.proc_strand_seg_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.strand_tex); + DRW_TEXTURE_FREE_SAFE(curves_cache.strand_seg_tex); + + for (int i = 0; i < MAX_HAIR_SUBDIV; i++) { + GPU_VERTBUF_DISCARD_SAFE(curves_cache.final[i].proc_buf); + DRW_TEXTURE_FREE_SAFE(curves_cache.final[i].proc_tex); + for (int j = 0; j < MAX_THICKRES; j++) { + GPU_BATCH_DISCARD_SAFE(curves_cache.final[i].proc_hairs[j]); + } + } +} + static void curves_batch_cache_clear(Curves &curves) { CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache); @@ -77,7 +101,8 @@ static void curves_batch_cache_clear(Curves &curves) return; } - particle_batch_cache_clear_hair(&cache->hair); + curves_batch_cache_clear_data(cache->curves_cache); + GPU_BATCH_DISCARD_SAFE(cache->edit_points); } @@ -116,10 +141,9 @@ void DRW_curves_batch_cache_free(Curves *curves) MEM_SAFE_FREE(curves->batch_cache); } -static void ensure_seg_pt_count(const Curves &curves, ParticleHairCache &curves_cache) +static void ensure_seg_pt_count(const Curves &curves, CurvesEvalCache &curves_cache) { - if ((curves_cache.pos != nullptr && curves_cache.indices != nullptr) || - (curves_cache.proc_point_buf != nullptr)) { + if (curves_cache.proc_point_buf != nullptr) { return; } @@ -169,7 +193,7 @@ static void curves_batch_cache_fill_segments_proc_pos(const Curves &curves_id, } static void curves_batch_cache_ensure_procedural_pos(Curves &curves, - ParticleHairCache &cache, + CurvesEvalCache &cache, GPUMaterial *gpu_material) { if (cache.proc_point_buf == nullptr || DRW_vbo_requested(cache.proc_point_buf)) { @@ -229,7 +253,7 @@ static void curves_batch_cache_fill_strands_data(const Curves &curves_id, } static void curves_batch_cache_ensure_procedural_strand_data(Curves &curves, - ParticleHairCache &cache) + CurvesEvalCache &cache) { GPUVertBufRaw data_step, seg_step; @@ -259,7 +283,7 @@ static void curves_batch_cache_ensure_procedural_strand_data(Curves &curves, cache.proc_strand_seg_buf); } -static void curves_batch_cache_ensure_procedural_final_points(ParticleHairCache &cache, int subdiv) +static void curves_batch_cache_ensure_procedural_final_points(CurvesEvalCache &cache, int subdiv) { /* Same format as point_tex. */ GPUVertFormat format = {0}; @@ -296,7 +320,7 @@ static void curves_batch_cache_fill_segments_indices(const Curves &curves, } static void curves_batch_cache_ensure_procedural_indices(Curves &curves, - ParticleHairCache &cache, + CurvesEvalCache &cache, const int thickness_res, const int subdiv) { @@ -330,7 +354,7 @@ static void curves_batch_cache_ensure_procedural_indices(Curves &curves, } bool curves_ensure_procedural_data(Object *object, - ParticleHairCache **r_hair_cache, + CurvesEvalCache **r_hair_cache, GPUMaterial *gpu_material, const int subdiv, const int thickness_res) @@ -339,30 +363,31 @@ bool curves_ensure_procedural_data(Object *object, Curves &curves = *static_cast<Curves *>(object->data); CurvesBatchCache &cache = curves_batch_cache_get(curves); - *r_hair_cache = &cache.hair; + *r_hair_cache = &cache.curves_cache; const int steps = 3; /* TODO: don't hard-code? */ (*r_hair_cache)->final[subdiv].strands_res = 1 << (steps + subdiv); /* Refreshed on combing and simulation. */ if ((*r_hair_cache)->proc_point_buf == nullptr) { - ensure_seg_pt_count(curves, cache.hair); - curves_batch_cache_ensure_procedural_pos(curves, cache.hair, gpu_material); + ensure_seg_pt_count(curves, cache.curves_cache); + curves_batch_cache_ensure_procedural_pos(curves, cache.curves_cache, gpu_material); need_ft_update = true; } /* Refreshed if active layer or custom data changes. */ if ((*r_hair_cache)->strand_tex == nullptr) { - curves_batch_cache_ensure_procedural_strand_data(curves, cache.hair); + curves_batch_cache_ensure_procedural_strand_data(curves, cache.curves_cache); } /* Refreshed only on subdiv count change. */ if ((*r_hair_cache)->final[subdiv].proc_buf == nullptr) { - curves_batch_cache_ensure_procedural_final_points(cache.hair, subdiv); + curves_batch_cache_ensure_procedural_final_points(cache.curves_cache, subdiv); need_ft_update = true; } if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == nullptr) { - curves_batch_cache_ensure_procedural_indices(curves, cache.hair, thickness_res, subdiv); + curves_batch_cache_ensure_procedural_indices( + curves, cache.curves_cache, thickness_res, subdiv); } return need_ft_update; @@ -385,10 +410,10 @@ void DRW_curves_batch_cache_create_requested(const Object *ob) CurvesBatchCache &cache = curves_batch_cache_get(*curves); if (DRW_batch_requested(cache.edit_points, GPU_PRIM_POINTS)) { - DRW_vbo_request(cache.edit_points, &cache.hair.proc_point_buf); + DRW_vbo_request(cache.edit_points, &cache.curves_cache.proc_point_buf); } - if (DRW_vbo_requested(cache.hair.proc_point_buf)) { - curves_batch_cache_ensure_procedural_pos(*curves, cache.hair, nullptr); + if (DRW_vbo_requested(cache.curves_cache.proc_point_buf)) { + curves_batch_cache_ensure_procedural_pos(*curves, cache.curves_cache, nullptr); } } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index c4fa60ef51d..e4aec17eb69 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -518,15 +518,17 @@ static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, domain = ATTR_DOMAIN_POINT; layer_i = CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, name); - layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_vdata, CD_MLOOPCOL, name) : - layer_i; + layer_i = layer_i == -1 ? + CustomData_get_named_layer_index(cd_vdata, CD_PROP_BYTE_COLOR, name) : + layer_i; if (layer_i == -1) { domain = ATTR_DOMAIN_CORNER; layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_ldata, CD_PROP_COLOR, name) : layer_i; - layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_ldata, CD_MLOOPCOL, name) : - layer_i; + layer_i = layer_i == -1 ? + CustomData_get_named_layer_index(cd_ldata, CD_PROP_BYTE_COLOR, name) : + layer_i; } /* Note: this is not the same as the layer_i below. */ @@ -610,17 +612,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } if (layer == -1) { - layer = CustomData_get_named_layer(cd_vdata, CD_MLOOPCOL, name); + layer = CustomData_get_named_layer(cd_vdata, CD_PROP_BYTE_COLOR, name); if (layer != -1) { - type = CD_MLOOPCOL; + type = CD_PROP_BYTE_COLOR; domain = ATTR_DOMAIN_POINT; } } if (layer == -1) { - layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); + layer = CustomData_get_named_layer(cd_ldata, CD_PROP_BYTE_COLOR, name); if (layer != -1) { - type = CD_MLOOPCOL; + type = CD_PROP_BYTE_COLOR; domain = ATTR_DOMAIN_CORNER; } } @@ -700,11 +702,11 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } /* Note: attr->type will always be CD_PROP_COLOR even for - * CD_MLOOPCOL layers, see node_shader_gpu_vertex_color in + * CD_PROP_BYTE_COLOR layers, see node_shader_gpu_vertex_color in * node_shader_vertex_color.cc. */ case CD_MCOL: - case CD_MLOOPCOL: + case CD_PROP_BYTE_COLOR: case CD_PROP_COLOR: { int vcol_bit = mesh_cd_calc_gpu_layers_vcol_used(&me_query, cd_vdata, cd_ldata, name); diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index c6b56723818..0f1ab967ca5 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -164,7 +164,7 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache) GPU_VERTBUF_DISCARD_SAFE(point_cache->pos); } -void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) +static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) { /* TODO: more granular update tagging. */ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf); @@ -822,10 +822,11 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV); render_uv = CustomData_get_render_layer(&psmd->mesh_final->ldata, CD_MLOOPUV); } - if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) { - cache->num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL); - active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL); - render_col = CustomData_get_render_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL); + if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR)) { + cache->num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, + CD_PROP_BYTE_COLOR); + active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR); + render_col = CustomData_get_render_layer(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR); } } @@ -891,7 +892,7 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit GPU_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]); char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i); + const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR, i); GPU_vertformat_safe_attr_name(name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); int n = 0; @@ -1162,9 +1163,9 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, num_uv_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPUV); active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV); } - if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) { - num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL); - active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL); + if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR)) { + num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR); + active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR); } } @@ -1195,7 +1196,8 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, for (int i = 0; i < num_col_layers; i++) { char uuid[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i); + const char *name = CustomData_get_layer_name( + &psmd->mesh_final->ldata, CD_PROP_BYTE_COLOR, i); GPU_vertformat_safe_attr_name(name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); BLI_snprintf(uuid, sizeof(uuid), "c%s", attr_safe_name); diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index d302140d9ac..779ac43178c 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -21,6 +21,8 @@ struct Object; struct ParticleSystem; struct RegionView3D; struct ViewLayer; +struct Scene; +struct DRWData; /* Keep in sync with globalsBlock in shaders */ BLI_STATIC_ASSERT_ALIGN(GlobalsUboStorage, 16) @@ -54,16 +56,12 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, struct DRWShadingGroup *shgrp, struct GPUMaterial *gpu_material); -struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object, - struct DRWShadingGroup *shgrp, - struct GPUMaterial *gpu_material); /** * \note Only valid after #DRW_hair_update(). */ struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md); -struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object); void DRW_hair_duplimat_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, @@ -73,6 +71,37 @@ void DRW_hair_init(void); void DRW_hair_update(void); void DRW_hair_free(void); +/* draw_curves.cc */ + +/** + * \note Only valid after #DRW_curves_update(). + */ +struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object); + +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_update(void); +void DRW_curves_free(void); + +/* draw_volume.cc */ + +/** + * Add attributes bindings of volume grids to an existing shading group. + * No draw call is added so the caller can decide how to use the data. + * \return nullptr if there is something to draw. + */ +struct DRWShadingGroup *DRW_shgroup_volume_create_sub(struct Scene *scene, + struct Object *ob, + struct DRWShadingGroup *shgrp, + struct GPUMaterial *gpu_material); + +void DRW_volume_init(struct DRWData *drw_data); +void DRW_volume_ubos_pool_free(void *pool); +void DRW_volume_free(void); + /* draw_fluid.c */ /* Fluid simulation. */ @@ -83,7 +112,9 @@ void DRW_fluid_ensure_flags(struct FluidModifierData *fmd); void DRW_fluid_ensure_range_field(struct FluidModifierData *fmd); void DRW_smoke_free(struct FluidModifierData *fmd); -void DRW_smoke_free_velocity(struct FluidModifierData *fmd); + +void DRW_smoke_init(struct DRWData *drw_data); +void DRW_smoke_exit(struct DRWData *drw_data); /* draw_common.c */ diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc new file mode 100644 index 00000000000..88118361115 --- /dev/null +++ b/source/blender/draw/intern/draw_curves.cc @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2017 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Contains procedural GPU hair drawing methods. + */ + +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" + +#include "DRW_render.h" + +#include "draw_hair_private.h" +#include "draw_shader.h" + +#ifndef __APPLE__ +# define USE_TRANSFORM_FEEDBACK +# define USE_COMPUTE_SHADERS +#endif + +BLI_INLINE eParticleRefineShaderType drw_curves_shader_type_get() +{ +#ifdef USE_COMPUTE_SHADERS + if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) { + return PART_REFINE_SHADER_COMPUTE; + } +#endif +#ifdef USE_TRANSFORM_FEEDBACK + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK; +#endif + return PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND; +} + +#ifndef USE_TRANSFORM_FEEDBACK +struct CurvesEvalCall { + struct CurvesEvalCall *next; + GPUVertBuf *vbo; + DRWShadingGroup *shgrp; + uint vert_len; +}; + +static CurvesEvalCall *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 = 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 *curves_eval_shader_get(CurvesEvalShader type) +{ + return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get()); +} + +void DRW_curves_init(void) +{ + /* Initialize legacy hair too, to avoid verbosity in callers. */ + DRW_hair_init(); + +#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS) + g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0); +#else + g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR); +#endif + + 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); + + g_dummy_vbo = GPU_vertbuf_create_with_format(&format); + + const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_vertbuf_data_alloc(g_dummy_vbo, 1); + GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert); + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(g_dummy_vbo); + + g_dummy_texture = GPU_texture_create_from_vertbuf("hair_dummy_attr", g_dummy_vbo); + } +} + +static void drw_curves_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, + CurvesEvalCache *cache, + const int subdiv) +{ + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_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 = 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; + } + } +} + +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) { + 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); +#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); +#endif + + drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv); + DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len); + } +} + +static CurvesEvalCache *drw_curves_cache_get(Object *object, + GPUMaterial *gpu_material, + int subdiv, + int thickness_res) +{ + CurvesEvalCache *cache; + bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); + + if (update) { + if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { + drw_curves_cache_update_compute(cache, subdiv); + } + else { + drw_curves_cache_update_transform_feedback(cache, subdiv); + } + } + return cache; +} + +GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + CurvesEvalCache *cache = drw_curves_cache_get(object, nullptr, subdiv, thickness_res); + + return cache->final[subdiv].proc_buf; +} + +DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, + DRWShadingGroup *shgrp_parent, + GPUMaterial *gpu_material) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int subdiv = scene->r.hair_subdiv; + int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; + + CurvesEvalCache *curves_cache = drw_curves_cache_get( + object, gpu_material, subdiv, thickness_res); + + DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); + + /* Fix issue with certain driver not drawing anything if there is no texture bound to + * "ac", "au", "u" or "c". */ + DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture); + DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); + + /* TODO: Generalize radius implementation for curves data type. */ + float hair_rad_shape = 1.0f; + float hair_rad_root = 0.005f; + float hair_rad_tip = 0.0f; + bool hair_close_tip = true; + + DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex); + if (curves_cache->length_tex) { + DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); + } + DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1); + DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); + DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); + DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); + DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); + /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass + * culling test. */ + GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; + DRW_shgroup_call_no_cull(shgrp, geom, object); + + return shgrp; +} + +void DRW_curves_update() +{ + /* Update legacy hair too, to avoid verbosity in callers. */ + DRW_hair_update(); + +#ifndef USE_TRANSFORM_FEEDBACK + /** + * Workaround to transform feedback not working on mac. + * On some system it crashes (see T58489) and on some other it renders garbage (see T60171). + * + * So instead of using transform feedback we render to a texture, + * read back the result to system memory and re-upload as VBO data. + * It is really not ideal performance wise, but it is the simplest + * and the most local workaround that still uses the power of the GPU. + */ + + if (g_tf_calls == nullptr) { + return; + } + + /* Search ideal buffer size. */ + uint max_size = 0; + for (CurvesEvalCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) { + max_size = max_ii(max_size, pr_call->vert_len); + } + + /* Create target Texture / Frame-buffer */ + /* Don't use max size as it can be really heavy and fail. + * 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, (DrawEngineType *)DRW_curves_update); + g_tf_target_height = height; + g_tf_target_width = width; + + GPUFrameBuffer *fb = nullptr; + GPU_framebuffer_ensure_config(&fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(tex), + }); + + float *data = static_cast<float *>( + MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer")); + + GPU_framebuffer_bind(fb); + while (g_tf_calls != nullptr) { + CurvesEvalCall *pr_call = g_tf_calls; + g_tf_calls = g_tf_calls->next; + + g_tf_id_offset = 0; + while (pr_call->vert_len > 0) { + 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); + /* 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); + GPU_vertbuf_update_sub(pr_call->vbo, + sizeof(float[4]) * g_tf_id_offset, + sizeof(float[4]) * max_read_px_len, + data); + + g_tf_id_offset += max_read_px_len; + pr_call->vert_len -= max_read_px_len; + } + + MEM_freeN(pr_call); + } + + MEM_freeN(data); + GPU_framebuffer_free(fb); +#else + /* Just render the pass when using compute shaders or transform feedback. */ + DRW_draw_pass(g_tf_pass); + if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + } +#endif +} + +void DRW_curves_free() +{ + DRW_hair_free(); + + GPU_VERTBUF_DISCARD_SAFE(g_dummy_vbo); + DRW_TEXTURE_FREE_SAFE(g_dummy_texture); +} diff --git a/source/blender/draw/intern/draw_curves_private.h b/source/blender/draw/intern/draw_curves_private.h new file mode 100644 index 00000000000..76d5f15319d --- /dev/null +++ b/source/blender/draw/intern/draw_curves_private.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2017 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_THICKRES 2 /* see eHairType */ +#define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */ + +typedef enum CurvesEvalShader { + CURVES_EVAL_CATMULL_ROM = 0, + CURVES_EVAL_BEZIER = 1, +} CurvesEvalShader; +#define CURVES_EVAL_SHADER_NUM 3 + +struct GPUVertBuf; +struct GPUIndexBuf; +struct GPUBatch; +struct GPUTexture; + +typedef struct CurvesEvalFinalCache { + /* Output of the subdivision stage: vertex buffer sized to subdiv level. */ + GPUVertBuf *proc_buf; + GPUTexture *proc_tex; + + /* Just contains a huge index buffer used to draw the final curves. */ + GPUBatch *proc_hairs[MAX_THICKRES]; + + /* Points per curve, at least 2. */ + int strands_res; +} CurvesEvalFinalCache; + +/* Curves procedural display: Evaluation is done on the GPU. */ +typedef struct CurvesEvalCache { + /* Input control points */ + GPUVertBuf *proc_point_buf; + GPUTexture *point_tex; + + /** Info of control points strands (segment count and base index) */ + GPUVertBuf *proc_strand_buf; + GPUTexture *strand_tex; + + /* Curve length data. */ + GPUVertBuf *proc_length_buf; + GPUTexture *length_tex; + + GPUVertBuf *proc_strand_seg_buf; + GPUTexture *strand_seg_tex; + + CurvesEvalFinalCache final[MAX_HAIR_SUBDIV]; + + int strands_len; + int elems_len; + int point_len; +} CurvesEvalCache; + +/** + * Ensure all textures and buffers needed for GPU accelerated drawing. + */ +bool curves_ensure_procedural_data(struct Object *object, + struct CurvesEvalCache **r_hair_cache, + struct GPUMaterial *gpu_material, + int subdiv, + int thickness_res); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_fluid.c b/source/blender/draw/intern/draw_fluid.c index 667bd24dddb..d3d4bbf505e 100644 --- a/source/blender/draw/intern/draw_fluid.c +++ b/source/blender/draw/intern/draw_fluid.c @@ -9,6 +9,7 @@ #include <string.h> +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -21,6 +22,8 @@ #include "GPU_texture.h" +#include "draw_manager.h" + #include "draw_common.h" /* Own include. */ #ifdef WITH_FLUID @@ -419,46 +422,6 @@ static bool get_smoke_velocity_field(FluidDomainSettings *fds, /** \name Public API * \{ */ -void DRW_smoke_free(FluidModifierData *fmd) -{ - if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { - if (fmd->domain->tex_density) { - GPU_texture_free(fmd->domain->tex_density); - fmd->domain->tex_density = NULL; - } - - if (fmd->domain->tex_color) { - GPU_texture_free(fmd->domain->tex_color); - fmd->domain->tex_color = NULL; - } - - if (fmd->domain->tex_shadow) { - GPU_texture_free(fmd->domain->tex_shadow); - fmd->domain->tex_shadow = NULL; - } - - if (fmd->domain->tex_flame) { - GPU_texture_free(fmd->domain->tex_flame); - fmd->domain->tex_flame = NULL; - } - - if (fmd->domain->tex_flame_coba) { - GPU_texture_free(fmd->domain->tex_flame_coba); - fmd->domain->tex_flame_coba = NULL; - } - - if (fmd->domain->tex_coba) { - GPU_texture_free(fmd->domain->tex_coba); - fmd->domain->tex_coba = NULL; - } - - if (fmd->domain->tex_field) { - GPU_texture_free(fmd->domain->tex_field); - fmd->domain->tex_field = NULL; - } - } -} - void DRW_smoke_ensure_coba_field(FluidModifierData *fmd) { #ifndef WITH_FLUID @@ -469,6 +432,7 @@ void DRW_smoke_ensure_coba_field(FluidModifierData *fmd) if (!fds->tex_field) { fds->tex_field = create_field_texture(fds, false); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_field)); } if (!fds->tex_coba && !ELEM(fds->coba_field, FLUID_DOMAIN_FIELD_PHI, @@ -478,6 +442,7 @@ void DRW_smoke_ensure_coba_field(FluidModifierData *fmd) FLUID_DOMAIN_FIELD_FLAGS, FLUID_DOMAIN_FIELD_PRESSURE)) { fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_coba)); } } #endif @@ -493,19 +458,24 @@ void DRW_smoke_ensure(FluidModifierData *fmd, int highres) if (!fds->tex_density) { fds->tex_density = create_density_texture(fds, highres); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_density)); } if (!fds->tex_color) { fds->tex_color = create_color_texture(fds, highres); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_color)); } if (!fds->tex_flame) { fds->tex_flame = create_flame_texture(fds, highres); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flame)); } if (!fds->tex_flame_coba && fds->tex_flame) { fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flame_coba)); } if (!fds->tex_shadow) { fds->tex_shadow = create_volume_texture( fds->res, GPU_R8, GPU_DATA_FLOAT, manta_smoke_get_shadow(fds->fluid)); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_shadow)); } } #endif /* WITH_FLUID */ @@ -536,6 +506,9 @@ void DRW_smoke_ensure_velocity(FluidModifierData *fmd) "vely", UNPACK3(fds->res), 1, GPU_R16F, GPU_DATA_FLOAT, vel_y); fds->tex_velocity_z = GPU_texture_create_3d( "velz", UNPACK3(fds->res), 1, GPU_R16F, GPU_DATA_FLOAT, vel_z); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_x)); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_y)); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_z)); } } #endif /* WITH_FLUID */ @@ -551,6 +524,7 @@ void DRW_fluid_ensure_flags(FluidModifierData *fmd) if (!fds->tex_flags) { fds->tex_flags = create_volume_texture( fds->res, GPU_R8UI, GPU_DATA_INT, manta_smoke_get_flags(fds->fluid)); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_flags)); swizzle_texture_channel_single(fds->tex_flags); } @@ -568,42 +542,29 @@ void DRW_fluid_ensure_range_field(FluidModifierData *fmd) if (!fds->tex_range_field) { fds->tex_range_field = create_field_texture(fds, true); + BLI_addtail(&DST.vmempool->smoke_textures, BLI_genericNodeN(&fds->tex_range_field)); } } #endif /* WITH_FLUID */ } -void DRW_smoke_free_velocity(FluidModifierData *fmd) +void DRW_smoke_init(DRWData *drw_data) { - /* TODO: Unify with the other #GPU_free_smoke. */ - - if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { - if (fmd->domain->tex_velocity_x) { - GPU_texture_free(fmd->domain->tex_velocity_x); - } - - if (fmd->domain->tex_velocity_y) { - GPU_texture_free(fmd->domain->tex_velocity_y); - } - - if (fmd->domain->tex_velocity_z) { - GPU_texture_free(fmd->domain->tex_velocity_z); - } - - if (fmd->domain->tex_flags) { - GPU_texture_free(fmd->domain->tex_flags); - } - - if (fmd->domain->tex_range_field) { - GPU_texture_free(fmd->domain->tex_range_field); - } + BLI_listbase_clear(&drw_data->smoke_textures); +} - fmd->domain->tex_velocity_x = NULL; - fmd->domain->tex_velocity_y = NULL; - fmd->domain->tex_velocity_z = NULL; - fmd->domain->tex_flags = NULL; - fmd->domain->tex_range_field = NULL; +void DRW_smoke_exit(DRWData *drw_data) +{ + /* Free Smoke Textures after rendering */ + /* XXX This is a waste of processing and GPU bandwidth if nothing + * is updated. But the problem is since Textures are stored in the + * modifier we don't want them to take precious VRAM if the + * modifier is not used for display. We should share them for + * all viewport in a redraw at least. */ + LISTBASE_FOREACH (LinkData *, link, &drw_data->smoke_textures) { + GPU_TEXTURE_FREE_SAFE(*(GPUTexture **)link->data); } + BLI_freelistN(&drw_data->smoke_textures); } /** \} */ diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index aac6f7e58c5..8351452769d 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -179,25 +179,6 @@ static ParticleHairCache *drw_hair_particle_cache_get(Object *object, return cache; } -static ParticleHairCache *drw_curves_cache_get(Object *object, - GPUMaterial *gpu_material, - int subdiv, - int thickness_res) -{ - ParticleHairCache *cache; - bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); - - if (update) { - if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) { - drw_hair_particle_cache_update_compute(cache, subdiv); - } - else { - drw_hair_particle_cache_update_transform_feedback(cache, subdiv); - } - } - return cache; -} - GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, ModifierData *md) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -212,19 +193,6 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi return cache->final[subdiv].proc_buf; } -GPUVertBuf *DRW_curves_pos_buffer_get(Object *object) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - int subdiv = scene->r.hair_subdiv; - int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - - ParticleHairCache *cache = drw_curves_cache_get(object, NULL, subdiv, thickness_res); - - return cache->final[subdiv].proc_buf; -} - void DRW_hair_duplimat_get(Object *object, ParticleSystem *UNUSED(psys), ModifierData *UNUSED(md), @@ -323,71 +291,6 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, return shgrp; } -DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object, - DRWShadingGroup *shgrp_parent, - GPUMaterial *gpu_material) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - int subdiv = scene->r.hair_subdiv; - int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - - ParticleHairCache *curves_cache = drw_curves_cache_get( - object, gpu_material, subdiv, thickness_res); - - DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); - - /* TODO: optimize this. Only bind the ones GPUMaterial needs. */ - for (int i = 0; i < curves_cache->num_uv_layers; i++) { - for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->uv_layer_names[i][n][0] != '\0'; n++) { - DRW_shgroup_uniform_texture( - shgrp, curves_cache->uv_layer_names[i][n], curves_cache->uv_tex[i]); - } - } - for (int i = 0; i < curves_cache->num_col_layers; i++) { - for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->col_layer_names[i][n][0] != '\0'; n++) { - DRW_shgroup_uniform_texture( - shgrp, curves_cache->col_layer_names[i][n], curves_cache->col_tex[i]); - } - } - - /* Fix issue with certain driver not drawing anything if there is no texture bound to - * "ac", "au", "u" or "c". */ - if (curves_cache->num_uv_layers == 0) { - DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture); - DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture); - } - if (curves_cache->num_col_layers == 0) { - DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture); - DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture); - } - - /* TODO: Generalize radius implementation for curves data type. */ - float hair_rad_shape = 1.0f; - float hair_rad_root = 0.005f; - float hair_rad_tip = 0.0f; - bool hair_close_tip = true; - - DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex); - if (curves_cache->length_tex) { - DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex); - } - DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1); - DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); - DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root); - DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip); - DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip); - /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass - * culling test. */ - GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1]; - DRW_shgroup_call_no_cull(shgrp, geom, object); - - return shgrp; -} - void DRW_hair_update(void) { #ifndef USE_TRANSFORM_FEEDBACK diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 58e8609106b..5d84c8863f2 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -75,8 +75,6 @@ typedef struct ParticleHairCache { int point_len; } ParticleHairCache; -void particle_batch_cache_clear_hair(struct ParticleHairCache *hair_cache); - /** * Ensure all textures and buffers needed for GPU accelerated drawing. */ @@ -88,15 +86,6 @@ bool particles_ensure_procedural_data(struct Object *object, int subdiv, int thickness_res); -/** - * Ensure all textures and buffers needed for GPU accelerated drawing. - */ -bool curves_ensure_procedural_data(struct Object *object, - struct ParticleHairCache **r_hair_cache, - struct GPUMaterial *gpu_material, - int subdiv, - int thickness_res); - #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 88d05fcd928..adf9d4a13df 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -478,6 +478,7 @@ void DRW_viewport_data_free(DRWData *drw_data) MEM_freeN(drw_data->matrices_ubo); MEM_freeN(drw_data->obinfos_ubo); } + DRW_volume_ubos_pool_free(drw_data->volume_grids_ubos); MEM_freeN(drw_data); } @@ -1649,7 +1650,9 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_globals_update(); drw_debug_init(); - DRW_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); /* No frame-buffer allowed before drawing. */ BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); @@ -1704,7 +1707,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, GPU_framebuffer_bind(DST.default_framebuffer); GPU_framebuffer_clear_depth_stencil(DST.default_framebuffer, 1.0f, 0xFF); - DRW_hair_update(); + DRW_curves_update(); DRW_draw_callbacks_pre_scene(); @@ -1715,6 +1718,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, GPU_flush(); } + DRW_smoke_exit(DST.vmempool); + DRW_stats_reset(); DRW_draw_callbacks_post_scene(); @@ -1999,6 +2004,8 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) GPU_framebuffer_restore(); + DRW_smoke_exit(DST.vmempool); + drw_manager_exit(&DST); /* Reset state after drawing */ @@ -2015,7 +2022,9 @@ 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_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); drw_task_graph_init(); const int object_type_exclude_viewport = draw_ctx->v3d ? @@ -2070,7 +2079,9 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, drw_manager_init(&DST, NULL, NULL); - DRW_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, draw_engine_type); @@ -2079,6 +2090,8 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, callback(data, user_data); DST.buffer_finish_called = false; + DRW_smoke_exit(DST.vmempool); + GPU_framebuffer_restore(); /* The use of custom pipeline in other thread using the same @@ -2095,11 +2108,15 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, void DRW_cache_restart(void) { + DRW_smoke_exit(DST.vmempool); + drw_manager_init(&DST, DST.viewport, (int[2]){UNPACK2(DST.size)}); DST.buffer_finish_called = false; - DRW_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); } void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, @@ -2416,7 +2433,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); { drw_engines_cache_init(); @@ -2493,7 +2512,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_state_reset(); DRW_draw_callbacks_pre_scene(); - DRW_hair_update(); + DRW_curves_update(); /* Only 1-2 passes. */ while (true) { @@ -2511,6 +2530,8 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } } + DRW_smoke_exit(DST.vmempool); + DRW_state_reset(); drw_engines_disable(); @@ -2586,7 +2607,9 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Init engines */ drw_engines_init(); - DRW_hair_init(); + DRW_curves_init(); + DRW_volume_init(DST.vmempool); + DRW_smoke_init(DST.vmempool); { drw_engines_cache_init(); @@ -2619,10 +2642,12 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* Start Drawing */ DRW_state_reset(); - DRW_hair_update(); + DRW_curves_update(); drw_engines_draw_scene(); + DRW_smoke_exit(DST.vmempool); + DRW_state_reset(); /* TODO: Reading depth for operators should be done here. */ @@ -3008,7 +3033,8 @@ void DRW_engines_free(void) GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer_depth_only); DRW_shaders_free(); - DRW_hair_free(); + DRW_curves_free(); + DRW_volume_free(); DRW_shape_cache_free(); DRW_stats_free(); DRW_globals_free(); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 832897b7040..8812f112014 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -527,6 +527,10 @@ typedef struct DRWData { struct GPUUniformBuf **obinfos_ubo; struct GHash *obattrs_ubo_pool; uint ubo_len; + /** Per draw-call volume object data. */ + void *volume_grids_ubos; /* VolumeUniformBufPool */ + /** List of smoke textures to free after drawing. */ + ListBase smoke_textures; /** Texture pool to reuse temp texture across engines. */ /* TODO(@fclem): The pool could be shared even between view-ports. */ struct DRWTexturePool *texture_pool; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 39f083aaf96..b5432da0957 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -15,6 +15,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_volume.h" #include "DNA_curve_types.h" #include "DNA_mesh_types.h" @@ -555,10 +556,19 @@ void DRW_shgroup_vertex_buffer_ref_ex(DRWShadingGroup *shgroup, static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4]) { ID *ob_data = (ob) ? ob->data : NULL; + float loc[3], size[3]; float *texcoloc = NULL; float *texcosize = NULL; if (ob_data != NULL) { switch (GS(ob_data->name)) { + case ID_VO: { + BoundBox *bbox = BKE_volume_boundbox_get(ob); + mid_v3_v3v3(loc, bbox->vec[0], bbox->vec[6]); + sub_v3_v3v3(size, bbox->vec[0], bbox->vec[6]); + texcoloc = loc; + texcosize = size; + break; + } case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); break; diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index 53da300c106..487a09d313d 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -92,6 +92,30 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, return e_data.hair_refine_sh[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; + switch (sh_type) { + case PART_REFINE_SHADER_COMPUTE: + sh = hair_refine_shader_compute_create(type); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK: + sh = hair_refine_shader_transform_feedback_create(type); + break; + case PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND: + sh = hair_refine_shader_transform_feedback_workaround_create(type); + break; + default: + BLI_assert_msg(0, "Incorrect shader type"); + } + e_data.hair_refine_sh[type] = sh; + } + + return e_data.hair_refine_sh[type]; +} + /** \} */ void DRW_shaders_free(void) diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 65b9cafc1d9..650e78c9362 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -7,6 +7,7 @@ #pragma once +#include "draw_curves_private.h" #include "draw_hair_private.h" #ifdef __cplusplus @@ -25,6 +26,10 @@ typedef enum eParticleRefineShaderType { struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); + +struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, + eParticleRefineShaderType sh_type); + void DRW_shaders_free(void); #ifdef __cplusplus diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index 58875c0496a..db128fffde7 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -6,12 +6,16 @@ typedef struct ViewInfos ViewInfos; typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; +typedef struct VolumeInfos VolumeInfos; #endif #define DRW_SHADER_SHARED_H #define DRW_RESOURCE_CHUNK_LEN 512 +/* Define the maximum number of grid we allow in a volume UBO. */ +#define DRW_GRID_PER_VOLUME_MAX 16 + struct ViewInfos { /* View matrices */ float4x4 persmat; @@ -63,6 +67,18 @@ struct ObjectInfos { }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) +struct VolumeInfos { + /* Object to grid-space. */ + float4x4 grids_xform[DRW_GRID_PER_VOLUME_MAX]; + /* NOTE: vec4 for alignment. Only float3 needed. */ + float4 color_mul; + float density_scale; + float temperature_mul; + float temperature_bias; + float _pad; +}; +BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 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_volume.cc b/source/blender/draw/intern/draw_volume.cc new file mode 100644 index 00000000000..8d9a6f486e2 --- /dev/null +++ b/source/blender/draw/intern/draw_volume.cc @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup draw + * + * \brief Contains Volume object GPU attributes configuration. + */ + +#include "DRW_gpu_wrapper.hh" +#include "DRW_render.h" + +#include "DNA_fluid_types.h" +#include "DNA_volume_types.h" + +#include "BKE_fluid.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_volume.h" +#include "BKE_volume_render.h" + +#include "GPU_material.h" + +#include "draw_common.h" +#include "draw_manager.h" + +using namespace blender; +using namespace blender::draw; +using VolumeInfosBuf = blender::draw::UniformBuffer<VolumeInfos>; + +static struct { + GPUTexture *dummy_zero; + GPUTexture *dummy_one; + float dummy_grid_mat[4][4]; +} g_data = {}; + +struct VolumeUniformBufPool { + Vector<VolumeInfosBuf *> ubos; + uint used = 0; + + ~VolumeUniformBufPool() + { + for (VolumeInfosBuf *ubo : ubos) { + delete ubo; + } + } + + void reset() + { + used = 0; + } + + VolumeInfosBuf *alloc() + { + if (used >= ubos.size()) { + VolumeInfosBuf *buf = new VolumeInfosBuf(); + ubos.append(buf); + return buf; + } + return ubos[used++]; + } +}; + +void DRW_volume_ubos_pool_free(void *pool) +{ + delete reinterpret_cast<VolumeUniformBufPool *>(pool); +} + +static void drw_volume_globals_init() +{ + const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + g_data.dummy_zero = GPU_texture_create_3d( + "dummy_zero", 1, 1, 1, 1, GPU_RGBA8, GPU_DATA_FLOAT, zero); + g_data.dummy_one = GPU_texture_create_3d( + "dummy_one", 1, 1, 1, 1, GPU_RGBA8, GPU_DATA_FLOAT, one); + GPU_texture_wrap_mode(g_data.dummy_zero, true, true); + GPU_texture_wrap_mode(g_data.dummy_one, true, true); + + memset(g_data.dummy_grid_mat, 0, sizeof(g_data.dummy_grid_mat)); +} + +void DRW_volume_free(void) +{ + GPU_TEXTURE_FREE_SAFE(g_data.dummy_zero); + GPU_TEXTURE_FREE_SAFE(g_data.dummy_one); +} + +static GPUTexture *grid_default_texture(eGPUDefaultValue default_value) +{ + switch (default_value) { + case GPU_DEFAULT_0: + return g_data.dummy_zero; + case GPU_DEFAULT_1: + return g_data.dummy_one; + } + return g_data.dummy_zero; +} + +void DRW_volume_init(DRWData *drw_data) +{ + if (drw_data->volume_grids_ubos == nullptr) { + drw_data->volume_grids_ubos = new VolumeUniformBufPool(); + } + VolumeUniformBufPool *pool = (VolumeUniformBufPool *)drw_data->volume_grids_ubos; + pool->reset(); + + if (g_data.dummy_one == nullptr) { + drw_volume_globals_init(); + } +} + +static DRWShadingGroup *drw_volume_object_grids_init(Object *ob, + ListBase *attrs, + DRWShadingGroup *grp) +{ + VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos; + VolumeInfosBuf &volume_infos = *pool->alloc(); + + Volume *volume = (Volume *)ob->data; + BKE_volume_load(volume, G.main); + + grp = DRW_shgroup_create_sub(grp); + + volume_infos.density_scale = BKE_volume_density_scale(volume, ob->obmat); + volume_infos.color_mul = float4(1.0f); + volume_infos.temperature_mul = 1.0f; + volume_infos.temperature_bias = 0.0f; + + /* Bind volume grid textures. */ + int grid_id = 0; + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) { + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, attr->name); + const DRWVolumeGrid *drw_grid = (volume_grid) ? + DRW_volume_batch_cache_get_grid(volume, volume_grid) : + nullptr; + + /* Handle 3 cases here: + * - Grid exists and texture was loaded -> use texture. + * - Grid exists but has zero size or failed to load -> use zero. + * - Grid does not exist -> use default value. */ + const GPUTexture *grid_tex = (drw_grid) ? drw_grid->texture : + (volume_grid) ? g_data.dummy_zero : + grid_default_texture(attr->default_value); + DRW_shgroup_uniform_texture(grp, attr->input_name, grid_tex); + + copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(), drw_grid->object_to_texture); + } + + volume_infos.push_update(); + + DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos); + + return grp; +} + +static DRWShadingGroup *drw_volume_object_mesh_init(Scene *scene, + Object *ob, + ListBase *attrs, + DRWShadingGroup *grp) +{ + VolumeUniformBufPool *pool = (VolumeUniformBufPool *)DST.vmempool->volume_grids_ubos; + VolumeInfosBuf &volume_infos = *pool->alloc(); + + ModifierData *md = nullptr; + + volume_infos.density_scale = 1.0f; + volume_infos.color_mul = float4(1.0f); + volume_infos.temperature_mul = 1.0f; + volume_infos.temperature_bias = 0.0f; + + /* Smoke Simulation */ + if ((md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) && + (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) && + ((FluidModifierData *)md)->domain != nullptr) { + FluidModifierData *fmd = (FluidModifierData *)md; + FluidDomainSettings *fds = fmd->domain; + + /* Don't try to show liquid domains here. */ + if (!fds->fluid || !(fds->type == FLUID_DOMAIN_TYPE_GAS)) { + return nullptr; + } + + if (fds->fluid && (fds->type == FLUID_DOMAIN_TYPE_GAS)) { + DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE); + } + + grp = DRW_shgroup_create_sub(grp); + + int grid_id = 0; + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) { + if (STREQ(attr->name, "density")) { + DRW_shgroup_uniform_texture_ref( + grp, attr->input_name, fds->tex_density ? &fds->tex_density : &g_data.dummy_one); + } + else if (STREQ(attr->name, "color")) { + DRW_shgroup_uniform_texture_ref( + grp, attr->input_name, fds->tex_color ? &fds->tex_color : &g_data.dummy_one); + } + else if (STR_ELEM(attr->name, "flame", "temperature")) { + DRW_shgroup_uniform_texture_ref( + grp, attr->input_name, fds->tex_flame ? &fds->tex_flame : &g_data.dummy_zero); + } + else { + DRW_shgroup_uniform_texture( + grp, attr->input_name, grid_default_texture(attr->default_value)); + } + copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(), g_data.dummy_grid_mat); + } + + bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 && + (fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0); + if (use_constant_color) { + volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f); + } + + /* Output is such that 0..1 maps to 0..1000K */ + volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition; + volume_infos.temperature_bias = fds->flame_ignition; + } + else { + grp = DRW_shgroup_create_sub(grp); + + int grid_id = 0; + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) { + DRW_shgroup_uniform_texture( + grp, attr->input_name, grid_default_texture(attr->default_value)); + copy_m4_m4(volume_infos.grids_xform[grid_id++].ptr(), g_data.dummy_grid_mat); + } + } + + volume_infos.push_update(); + + DRW_shgroup_uniform_block(grp, "drw_volume", volume_infos); + + return grp; +} + +static DRWShadingGroup *drw_volume_world_grids_init(ListBase *attrs, DRWShadingGroup *grp) +{ + /* Bind default volume grid textures. */ + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attrs) { + DRW_shgroup_uniform_texture(grp, attr->input_name, grid_default_texture(attr->default_value)); + } + return grp; +} + +DRWShadingGroup *DRW_shgroup_volume_create_sub(Scene *scene, + Object *ob, + DRWShadingGroup *shgrp, + GPUMaterial *gpu_material) +{ + ListBase attrs = GPU_material_attributes(gpu_material); + + if (ob == nullptr) { + return drw_volume_world_grids_init(&attrs, shgrp); + } + if (ob->type == OB_VOLUME) { + return drw_volume_object_grids_init(ob, &attrs, shgrp); + } + return drw_volume_object_mesh_init(scene, ob, &attrs, shgrp); +} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index d52226a4c90..4cb68cad66c 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -173,7 +173,7 @@ static void extract_vcol_init(const MeshRenderData *mr, } BMIter iter; - const bool is_byte = ref.layer->type == CD_MLOOPCOL; + const bool is_byte = ref.layer->type == CD_PROP_BYTE_COLOR; const bool is_point = ref.domain == ATTR_DOMAIN_POINT; BMFace *f; @@ -318,7 +318,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, BMIter iter; BMFace *f; int cd_ofs = cdata->layers[layer_i].offset; - const bool is_byte = ref.layer->type == CD_MLOOPCOL; + const bool is_byte = ref.layer->type == CD_PROP_BYTE_COLOR; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { BMLoop *l_iter = f->l_first; diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl index 99db2929a13..30239a84c0c 100644 --- a/source/blender/draw/intern/shaders/common_attribute_lib.glsl +++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl @@ -19,3 +19,15 @@ vec4 attr_load_vec4(samplerBuffer attr); vec3 attr_load_vec3(samplerBuffer attr); vec2 attr_load_vec2(samplerBuffer attr); float attr_load_float(samplerBuffer attr); + +vec3 attr_load_orco(sampler3D orco); +vec4 attr_load_tangent(sampler3D tangent); +vec3 attr_load_uv(sampler3D attr); +vec4 attr_load_color(sampler3D tex); +vec4 attr_load_vec4(sampler3D tex); +vec3 attr_load_vec3(sampler3D tex); +vec2 attr_load_vec2(sampler3D tex); +float attr_load_float(sampler3D tex); + +float attr_load_temperature_post(float attr); +vec4 attr_load_color_post(vec4 attr); 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 392b016fc3b..c74a043ec97 100644 --- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh +++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh @@ -6,3 +6,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos) .typedef_source("draw_shader_shared.h") .define("OBINFO_LIB") .uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH); + +GPU_SHADER_CREATE_INFO(draw_volume_infos) + .typedef_source("draw_shader_shared.h") + .uniform_buf(2, "VolumeInfos", "drw_volume", Frequency::BATCH); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 5b742ddf272..14a3b958ea6 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1126,17 +1126,17 @@ static void get_keyframe_values_create_reports(ReportList *reports, for (int i = 0; i < count; i++) { const bool cur_index_evaluated = ELEM(index, i, -1) || force_all; if (!cur_index_evaluated) { - /* values[i] was never intended to be remapped. */ + /* `values[i]` was never intended to be remapped. */ continue; } if (BLI_BITMAP_TEST_BOOL(successful_remaps, i)) { - /* values[i] succesfully remapped. */ + /* `values[i]` successfully remapped. */ continue; } total_failed++; - /* Report that values[i] were intended to be remapped but failed remapping process. */ + /* Report that `values[i]` were intended to be remapped but failed remapping process. */ BLI_dynstr_appendf(ds_failed_indices, "%d, ", i); } diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 467b8efa622..452f1df86fb 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -241,7 +241,6 @@ enum class ConvertAttributeMode { Generic, UVMap, VertexGroup, - VertexColor, }; static bool geometry_attribute_convert_poll(bContext *C) @@ -288,7 +287,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) const CustomDataType dst_type = static_cast<CustomDataType>( RNA_enum_get(op->ptr, "data_type")); - if (ELEM(dst_type, CD_PROP_STRING, CD_MLOOPCOL)) { + if (ELEM(dst_type, CD_PROP_STRING)) { BKE_report(op->reports, RPT_ERROR, "Cannot convert to the selected type"); return OPERATOR_CANCELLED; } @@ -314,20 +313,6 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) &mesh->ldata, CD_MLOOPUV, CD_ASSIGN, dst_uvs, mesh->totloop, name.c_str()); break; } - case ConvertAttributeMode::VertexColor: { - MLoopCol *dst_colors = static_cast<MLoopCol *>( - MEM_calloc_arrayN(mesh->totloop, sizeof(MLoopCol), __func__)); - VArray<ColorGeometry4f> src_varray = mesh_component.attribute_get_for_read<ColorGeometry4f>( - name, ATTR_DOMAIN_CORNER, ColorGeometry4f{0.0f, 0.0f, 0.0f, 1.0f}); - for (const int i : IndexRange(mesh->totloop)) { - ColorGeometry4b encoded_color = src_varray[i].encode(); - copy_v4_v4_uchar(&dst_colors[i].r, &encoded_color.r); - } - mesh_component.attribute_try_delete(name); - CustomData_add_layer_named( - &mesh->ldata, CD_MLOOPCOL, CD_ASSIGN, dst_colors, mesh->totloop, name.c_str()); - break; - } case ConvertAttributeMode::VertexGroup: { Array<float> src_weights(mesh->totvert); VArray<float> src_varray = mesh_component.attribute_get_for_read<float>( @@ -385,7 +370,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) {0, nullptr, 0, nullptr, nullptr}}; static EnumPropertyItem types[3] = {{CD_PROP_COLOR, "COLOR", 0, "Color", ""}, - {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", ""}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", ""}, {0, nullptr, 0, nullptr, nullptr}}; prop = RNA_def_enum(ot->srna, @@ -415,9 +400,9 @@ static int geometry_color_attribute_set_render_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "name", name); CustomDataLayer *layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT); - layer = !layer ? BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_POINT) : layer; + layer = !layer ? BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_POINT) : layer; layer = !layer ? BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER) : layer; - layer = !layer ? BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_CORNER) : layer; + layer = !layer ? BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_CORNER) : layer; if (layer) { BKE_id_attributes_render_color_set(id, layer); @@ -550,7 +535,6 @@ void GEOMETRY_OT_attribute_convert(wmOperatorType *ot) {int(ConvertAttributeMode::Generic), "GENERIC", 0, "Generic", ""}, {int(ConvertAttributeMode::UVMap), "UV_MAP", 0, "UV Map", ""}, {int(ConvertAttributeMode::VertexGroup), "VERTEX_GROUP", 0, "Vertex Group", ""}, - {int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Color Attribute", ""}, {0, nullptr, 0, nullptr, nullptr}, }; diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index e4cb6d149f5..54aa5d16941 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -99,7 +99,7 @@ static void cage2d_draw_box_corners(const rctf *r, const float color[3], const float line_width) { - /* 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(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); @@ -442,7 +442,7 @@ static void imm_draw_point_aspect_2d( uint pos, float x, float y, float rad_x, float rad_y, bool solid) { if (solid) { - /* Note(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Trianglestrip. + /* NOTE(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Triangle-strip. */ immBegin(GPU_PRIM_TRIS, 6); immVertex2f(pos, x - rad_x, y - rad_y); @@ -455,7 +455,7 @@ static void imm_draw_point_aspect_2d( immEnd(); } else { - /* Note(Metal/AMD): Small Line-list primitives more optimal for GPU HW than Linestrip. */ + /* NOTE(Metal/AMD): Small Line-list primitives more optimal for GPU HW than Line-strip. */ immBegin(GPU_PRIM_LINES, 8); immVertex2f(pos, x - rad_x, y - rad_y); immVertex2f(pos, x - rad_x, y + rad_y); @@ -479,7 +479,7 @@ static void cage2d_draw_circle_wire(const rctf *r, const int draw_options, const float line_width) { - /* Note(Metal): Prefer using 3D coordinates with 3D shader input, even if rendering 2D gizmo's. + /* NOTE(Metal): Prefer using 3D coordinates with 3D shader input, even if rendering 2D gizmo's. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -491,7 +491,7 @@ static void cage2d_draw_circle_wire(const rctf *r, immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", line_width * U.pixelsize); - /* Small 'lines' primitives more efficient for hardware processing than linestrip. */ + /* Small 'lines' primitives more efficient for hardware processing than line-strip. */ immBegin(GPU_PRIM_LINES, 8); immVertex3f(pos, r->xmin, r->ymin, 0.0f); immVertex3f(pos, r->xmax, r->ymin, 0.0f); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 93a4a784674..7c7f532f087 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1313,7 +1313,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); - /* In Multiframe mode, duplicate the stroke in other frames. */ + /* In multi-frame mode, duplicate the stroke in other frames. */ if (GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd)) { const bool tail = (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK); BKE_gpencil_stroke_copy_to_keyframes(gpd, gpl, p->gpf, gps, tail); diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index fd4a28240fa..c69698f3f73 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -18,7 +18,7 @@ struct bContext; void ED_spacetypes_init(void); void ED_spacemacros_init(void); -/* the pluginnable API for export to editors */ +/* The plugin-able API for export to editors. */ /* -------------------------------------------------------------------- */ /** \name Calls for registering default spaces diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index a5e2e9353bf..4e6437e043a 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -13,6 +13,7 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_curveprofile_types.h" #include "DNA_gpencil_modifier_types.h" @@ -588,6 +589,234 @@ void UI_context_active_but_prop_get_templateID(bContext *C, } } +static void template_id_liboverride_hierarchy_collection_root_find_recursive( + Collection *collection, + const int parent_level, + Collection **r_collection_parent_best, + int *r_parent_level_best) +{ + if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + return; + } + if (ID_IS_OVERRIDABLE_LIBRARY(collection) || ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + if (parent_level > *r_parent_level_best) { + *r_parent_level_best = parent_level; + *r_collection_parent_best = collection; + } + } + for (CollectionParent *iter = collection->parents.first; iter != NULL; iter = iter->next) { + if (iter->collection->id.lib != collection->id.lib && ID_IS_LINKED(iter->collection)) { + continue; + } + template_id_liboverride_hierarchy_collection_root_find_recursive( + iter->collection, parent_level + 1, r_collection_parent_best, r_parent_level_best); + } +} + +static void template_id_liboverride_hierarchy_collections_tag_recursive( + Collection *root_collection, ID *target_id, const bool do_parents) +{ + root_collection->id.tag |= LIB_TAG_DOIT; + + /* Tag all local parents of the root collection, so that usages of the root collection and other + * linked ones can be replaced by the local overrides in those parents too. */ + if (do_parents) { + for (CollectionParent *iter = root_collection->parents.first; iter != NULL; + iter = iter->next) { + if (ID_IS_LINKED(iter->collection)) { + continue; + } + iter->collection->id.tag |= LIB_TAG_DOIT; + } + } + + for (CollectionChild *iter = root_collection->children.first; iter != NULL; iter = iter->next) { + if (iter->collection->id.lib != root_collection->id.lib && ID_IS_LINKED(root_collection)) { + continue; + } + if (ID_IS_LINKED(iter->collection) && iter->collection->id.lib != target_id->lib) { + continue; + } + if (GS(target_id->name) == ID_OB && + !BKE_collection_has_object_recursive(iter->collection, (Object *)target_id)) { + continue; + } + if (GS(target_id->name) == ID_GR && + !BKE_collection_has_collection(iter->collection, (Collection *)target_id)) { + continue; + } + template_id_liboverride_hierarchy_collections_tag_recursive( + iter->collection, target_id, false); + } +} + +static void template_id_liboverride_hierarchy_create(bContext *C, + Main *bmain, + TemplateID *template_ui, + PointerRNA *idptr, + const char **r_undo_push_label) +{ + ID *id = idptr->data; + ID *owner_id = template_ui->ptr.owner_id; + + /* Attempt to perform a hierarchy override, based on contextual data available. + * NOTE: do not attempt to perform such hierarchy override at all cost, if there is not enough + * context, better to abort than create random overrides all over the place. */ + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id)) { + return; + } + + Object *object_active = CTX_data_active_object(C); + if (object_active == NULL && GS(owner_id->name) == ID_OB) { + object_active = (Object *)owner_id; + } + if (object_active != NULL) { + if (ID_IS_LINKED(object_active)) { + if (object_active->id.lib != id->lib || + !ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(object_active)) { + /* The active object is from a different library than the overridden ID, or otherwise + * cannot be used in hierarchy. */ + object_active = NULL; + } + } + else if (!ID_IS_OVERRIDE_LIBRARY_REAL(object_active)) { + /* Fully local object cannot be used in override hierarchy either. */ + object_active = NULL; + } + } + + Collection *collection_active = CTX_data_collection(C); + if (collection_active == NULL && GS(owner_id->name) == ID_GR) { + collection_active = (Collection *)owner_id; + } + if (collection_active != NULL) { + if (ID_IS_LINKED(collection_active)) { + if (collection_active->id.lib != id->lib || + !ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(collection_active)) { + /* The active collection is from a different library than the overridden ID, or otherwise + * cannot be used in hierarchy. */ + collection_active = NULL; + } + else { + int parent_level_best = -1; + Collection *collection_parent_best = NULL; + template_id_liboverride_hierarchy_collection_root_find_recursive( + collection_active, 0, &collection_parent_best, &parent_level_best); + collection_active = collection_parent_best; + } + } + else if (!ID_IS_OVERRIDE_LIBRARY_REAL(collection_active)) { + /* Fully local collection cannot be used in override hierarchy either. */ + collection_active = NULL; + } + } + if (collection_active == NULL && object_active != NULL && + (ID_IS_LINKED(object_active) || ID_IS_OVERRIDE_LIBRARY_REAL(object_active))) { + /* If we failed to find a valid 'active' collection so far for our override hierarchy, but do + * have a valid 'active' object, try to find a collection from that object. */ + LISTBASE_FOREACH (Collection *, collection_iter, &bmain->collections) { + if (!(ID_IS_LINKED(collection_iter) || ID_IS_OVERRIDE_LIBRARY_REAL(collection_iter))) { + continue; + } + if (!BKE_collection_has_object_recursive(collection_iter, object_active)) { + continue; + } + int parent_level_best = -1; + Collection *collection_parent_best = NULL; + template_id_liboverride_hierarchy_collection_root_find_recursive( + collection_iter, 0, &collection_parent_best, &parent_level_best); + collection_active = collection_parent_best; + break; + } + } + + ID *id_override = NULL; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + switch (GS(id->name)) { + case ID_GR: + if (collection_active != NULL && + BKE_collection_has_collection(collection_active, (Collection *)id)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection_active, id, true); + 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); + } + else if (object_active != NULL && !ID_IS_LINKED(object_active) && + &object_active->instance_collection->id == id) { + object_active->id.tag |= LIB_TAG_DOIT; + BKE_lib_override_library_create(bmain, + scene, + view_layer, + id->lib, + id, + &object_active->id, + &object_active->id, + &id_override); + } + break; + case ID_OB: + if (collection_active != NULL && + BKE_collection_has_object_recursive(collection_active, (Object *)id)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection_active, id, true); + 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); + } + break; + case ID_ME: + case ID_CU_LEGACY: + case ID_MB: + case ID_LT: + case ID_LA: + case ID_CA: + case ID_SPK: + case ID_AR: + case ID_GD: + case ID_CV: + case ID_PT: + case ID_VO: + if (object_active != NULL && object_active->data == id) { + if (collection_active != NULL && + BKE_collection_has_object_recursive(collection_active, object_active)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection_active, id, true); + 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); + } + 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); + } + } + break; + case ID_MA: + case ID_TE: + case ID_IM: + break; + case ID_WO: + break; + case ID_PA: + break; + default: + break; + } + if (id_override != NULL) { + id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + *r_undo_push_label = "Make Library Override Hierarchy"; + + WM_event_add_notifier(C, NC_WINDOW, NULL); + DEG_relations_tag_update(bmain); + } +} + static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) { TemplateID *template_ui = (TemplateID *)arg_litem; @@ -637,33 +866,8 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) if (id) { Main *bmain = CTX_data_main(C); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - if (ID_IS_OVERRIDABLE_LIBRARY(id)) { - /* Only remap that specific ID usage to overriding local data-block. */ - ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); - if (override_id != NULL) { - BKE_main_id_newptr_and_tag_clear(bmain); - - if (GS(override_id->name) == ID_OB) { - Scene *scene = CTX_data_scene(C); - if (!BKE_collection_has_object_recursive(scene->master_collection, - (Object *)override_id)) { - BKE_collection_object_add_from( - bmain, scene, (Object *)id, (Object *)override_id); - } - } - - /* Assign new pointer, takes care of updates/notifiers */ - RNA_id_pointer_create(override_id, &idptr); - /* Insert into override hierarchy if possible. */ - ID *owner_id = template_ui->ptr.owner_id; - if (owner_id != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) { - override_id->override_library->hierarchy_root = - owner_id->override_library->hierarchy_root; - owner_id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY; - } - } - undo_push_label = "Make Library Override"; - } + template_id_liboverride_hierarchy_create( + C, bmain, template_ui, &idptr, &undo_push_label); } else { if (BKE_lib_id_make_local(bmain, id, 0)) { @@ -1006,6 +1210,7 @@ static void template_ID(const bContext *C, } if (ID_IS_LINKED(id)) { + const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); if (id->tag & LIB_TAG_INDIRECT) { but = uiDefIconBut(block, UI_BTYPE_BUT, @@ -1020,12 +1225,10 @@ static void template_ID(const bContext *C, 0, 0, 0, - TIP_("Indirect library data-block, cannot change")); - UI_but_flag_enable(but, UI_BUT_DISABLED); + TIP_("Indirect library data-block, cannot be made local, " + "Shift + Click to create a library override hierarchy")); } else { - const bool disabled = (!BKE_idtype_idcode_is_localizable(GS(id->name)) || - (idfrom && idfrom->lib)); but = uiDefIconBut(block, UI_BTYPE_BUT, 0, @@ -1041,13 +1244,13 @@ static void template_ID(const bContext *C, 0, TIP_("Direct linked library data-block, click to make local, " "Shift + Click to create a library override")); - if (disabled) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else { - UI_but_funcN_set( - but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); - } + } + if (disabled) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else { + UI_but_funcN_set( + but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); } } else if (ID_IS_OVERRIDE_LIBRARY(id)) { diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index beed4abd52b..3345d422bd1 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -438,7 +438,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) 0.0f, 1000.0f, "Clamp Bounding Box", - "Resize the objects to keep bounding box under this value. Value 0 diables clamping", + "Resize the objects to keep bounding box under this value. Value 0 disables clamping", 0.0f, 1000.0f); RNA_def_enum(ot->srna, diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index e3c3f1c38a0..0bd054e1b89 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -319,7 +319,7 @@ static int select_exec(bContext *C, wmOperator *op) ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_FINISHED; + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } MaskSplinePointUW *uw; @@ -361,14 +361,14 @@ static int select_exec(bContext *C, wmOperator *op) ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_FINISHED; + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } if (deselect_all) { /* For clip editor tracks, leave deselect all to clip editor. */ if (!ED_clip_can_select(C)) { ED_mask_deselect_all(C); ED_mask_view_lock_state_restore_no_jump(C, &lock_state); - return OPERATOR_FINISHED; + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } } @@ -386,7 +386,9 @@ static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_float_set_array(op->ptr, "location", co); - return select_exec(C, op); + const int retval = select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void MASK_OT_select(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index d3fc83eedd7..7634ce6af9e 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -230,8 +230,6 @@ static int geometry_extract_apply(bContext *C, } } - BKE_mesh_calc_normals(new_ob->data); - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob); BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_relations_tag_update(bmain); @@ -551,7 +549,6 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert); BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob, &CD_MASK_MESH, true); - BKE_mesh_calc_normals(new_ob->data); BKE_mesh_copy_parameters_for_eval(new_ob->data, mesh); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob); BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -561,7 +558,6 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) } BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob, &CD_MASK_MESH, true); - BKE_mesh_calc_normals(ob->data); if (ob->mode == OB_MODE_SCULPT) { SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 7feb04b3672..c3d5f33705c 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -937,7 +937,7 @@ bool EDBM_uv_check(BMEditMesh *em) bool EDBM_vert_color_check(BMEditMesh *em) { /* some of these checks could be a touch overkill */ - return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_MLOOPCOL); + return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_BYTE_COLOR); } /** \} */ diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 6f5c9d410c7..d11f0b490c1 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -109,7 +109,7 @@ static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer) int layer_index, tot, n; char htype = BM_FACE; - if (ELEM(type, CD_MLOOPCOL, CD_MLOOPUV)) { + if (ELEM(type, CD_PROP_BYTE_COLOR, CD_MLOOPUV)) { htype = BM_LOOP; } else if (ELEM(type, CD_PROP_COLOR)) { @@ -379,25 +379,25 @@ int ED_mesh_color_add( if (me->edit_mesh) { em = me->edit_mesh; - layernum = CustomData_number_of_layers(&em->bm->ldata, CD_MLOOPCOL); + layernum = CustomData_number_of_layers(&em->bm->ldata, CD_PROP_BYTE_COLOR); if (layernum >= MAX_MCOL) { BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i vertex color layers", MAX_MCOL); return -1; } - /* CD_MLOOPCOL */ - BM_data_layer_add_named(em->bm, &em->bm->ldata, CD_MLOOPCOL, name); + /* CD_PROP_BYTE_COLOR */ + BM_data_layer_add_named(em->bm, &em->bm->ldata, CD_PROP_BYTE_COLOR, name); /* copy data from active vertex color layer */ if (layernum && do_init) { - const int layernum_dst = CustomData_get_active_layer(&em->bm->ldata, CD_MLOOPCOL); - BM_data_layer_copy(em->bm, &em->bm->ldata, CD_MLOOPCOL, layernum_dst, layernum); + const int layernum_dst = CustomData_get_active_layer(&em->bm->ldata, CD_PROP_BYTE_COLOR); + BM_data_layer_copy(em->bm, &em->bm->ldata, CD_PROP_BYTE_COLOR, layernum_dst, layernum); } if (active_set || layernum == 0) { - CustomData_set_layer_active(&em->bm->ldata, CD_MLOOPCOL, layernum); + CustomData_set_layer_active(&em->bm->ldata, CD_PROP_BYTE_COLOR, layernum); } } else { - layernum = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + layernum = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR); if (layernum >= MAX_MCOL) { BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i vertex color layers", MAX_MCOL); return -1; @@ -405,14 +405,15 @@ int ED_mesh_color_add( if (me->mloopcol && do_init) { CustomData_add_layer_named( - &me->ldata, CD_MLOOPCOL, CD_DUPLICATE, me->mloopcol, me->totloop, name); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DUPLICATE, me->mloopcol, me->totloop, name); } else { - CustomData_add_layer_named(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, name); + CustomData_add_layer_named( + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, me->totloop, name); } if (active_set || layernum == 0) { - CustomData_set_layer_active(&me->ldata, CD_MLOOPCOL, layernum); + CustomData_set_layer_active(&me->ldata, CD_PROP_BYTE_COLOR, layernum); } BKE_mesh_update_customdata_pointers(me, true); @@ -427,18 +428,20 @@ int ED_mesh_color_add( bool ED_mesh_color_ensure(struct Mesh *me, const char *name) { BLI_assert(me->edit_mesh == NULL); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); - if (!me->mloopcol && me->totloop) { - CustomData_add_layer_named(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, name); - int layer_i = CustomData_get_layer_index(&me->ldata, CD_MLOOPCOL); + if (!layer) { + CustomData_add_layer_named( + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, NULL, 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, me->ldata.layers + layer_i); + BKE_id_attributes_active_color_set(&me->id, layer); BKE_mesh_update_customdata_pointers(me, true); } DEG_id_tag_update(&me->id, 0); - return (me->mloopcol != NULL); + return (layer != NULL); } bool ED_mesh_color_remove_index(Mesh *me, const int n) @@ -447,7 +450,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n) CustomDataLayer *cdl; int index; - index = CustomData_get_layer_index_n(ldata, CD_MLOOPCOL, n); + index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n); cdl = (index == -1) ? NULL : &ldata->layers[index]; if (!cdl) { @@ -463,7 +466,7 @@ bool ED_mesh_color_remove_index(Mesh *me, const int n) bool ED_mesh_color_remove_active(Mesh *me) { CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_active_layer(ldata, CD_MLOOPCOL); + const int n = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); if (n != -1) { return ED_mesh_color_remove_index(me, n); } @@ -472,7 +475,7 @@ bool ED_mesh_color_remove_active(Mesh *me) bool ED_mesh_color_remove_named(Mesh *me, const char *name) { CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_named_layer(ldata, CD_MLOOPCOL, name); + const int n = CustomData_get_named_layer(ldata, CD_PROP_BYTE_COLOR, name); if (n != -1) { return ED_mesh_color_remove_index(me, n); } @@ -715,7 +718,7 @@ static bool vertex_color_remove_poll(bContext *C) Object *ob = ED_object_context(C); Mesh *me = ob->data; CustomData *ldata = GET_CD_DATA(me, ldata); - const int active = CustomData_get_active_layer(ldata, CD_MLOOPCOL); + const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); if (active != -1) { return true; } @@ -1092,7 +1095,8 @@ void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_lo /* Default state is not to have tessface's so make sure this is the case. */ BKE_mesh_tessface_clear(mesh); - BKE_mesh_calc_normals(mesh); + /* Tag lazily calculated data as dirty. */ + BKE_mesh_normals_tag_dirty(mesh); DEG_id_tag_update(&mesh->id, 0); WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index d57471b658c..9575fde68f0 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -681,8 +681,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* tessface data removed above, no need to update */ BKE_mesh_update_customdata_pointers(me, false); - /* update normals in case objects with non-uniform scale are joined */ - BKE_mesh_calc_normals(me); + /* Tag normals dirty because vertex positions could be changed from the original. */ + BKE_mesh_normals_tag_dirty(me); /* old material array */ for (a = 1; a <= ob->totcol; a++) { diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index b3c5879b4d0..508d452bfe4 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1638,7 +1638,7 @@ struct CollectionAddInfo { ushort local_view_bits; /* The transform that should be applied to the collection, determined through operator properties * if set (e.g. to place the collection under the cursor), otherwise through context (e.g. 3D - * cursor location). */ + * cursor location). */ float loc[3], rot[3]; }; diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index d469efbd0a1..1483c24ac70 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -172,28 +172,35 @@ static bool multiresbake_check(bContext *C, wmOperator *op) ok = false; } else { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; - if (!ibuf) { - BKE_report(op->reports, RPT_ERROR, "Baking should happen to image with image buffer"); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - ok = false; - } - else { - if (ibuf->rect == NULL && ibuf->rect_float == NULL) { - ok = false; - } + if (!ibuf) { + BKE_report( + op->reports, RPT_ERROR, "Baking should happen to image with image buffer"); - if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) { ok = false; } - - if (!ok) { - BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type"); + else { + if (ibuf->rect == NULL && ibuf->rect_float == NULL) { + ok = false; + } + + if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) { + ok = false; + } + + if (!ok) { + BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type"); + } } - } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, NULL); + } } } } @@ -274,21 +281,27 @@ static void clear_single_image(Image *image, ClearFlag flag) const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f}; if ((image->id.tag & LIB_TAG_DOIT) == 0) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; - if (flag == CLEAR_TANGENT_NORMAL) { - IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid); - } - else if (flag == CLEAR_DISPLACEMENT) { - IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid); - } - else { - IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); - } + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, NULL); - image->id.tag |= LIB_TAG_DOIT; + if (flag == CLEAR_TANGENT_NORMAL) { + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid); + } + else if (flag == CLEAR_DISPLACEMENT) { + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid); + } + else { + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); + } + + image->id.tag |= LIB_TAG_DOIT; - BKE_image_release_ibuf(image, ibuf, NULL); + BKE_image_release_ibuf(image, ibuf, NULL); + } } } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 3060a1ecf62..a7379d7e492 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -19,6 +19,7 @@ #include "BLI_fileops.h" #include "BLI_listbase.h" #include "BLI_path_util.h" +#include "BLI_string.h" #include "BKE_context.h" #include "BKE_global.h" @@ -165,6 +166,7 @@ static void bake_update_image(ScrArea *area, Image *image) } static bool write_internal_bake_pixels(Image *image, + const int image_tile_number, BakePixel pixel_array[], float *buffer, const int width, @@ -174,7 +176,8 @@ static bool write_internal_bake_pixels(Image *image, const bool is_clear, const bool is_noncolor, Mesh const *mesh_eval, - char const *uv_layer) + char const *uv_layer, + const float uv_offset[2]) { ImBuf *ibuf; void *lock; @@ -182,7 +185,10 @@ static bool write_internal_bake_pixels(Image *image, char *mask_buffer = NULL; const size_t pixels_num = (size_t)width * (size_t)height; - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = image_tile_number; + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (!ibuf) { return false; @@ -270,7 +276,7 @@ static bool write_internal_bake_pixels(Image *image, /* margins */ if (margin > 0) { - RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer); + RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer, uv_offset); } ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; @@ -303,10 +309,8 @@ static void bake_targets_refresh(BakeTargets *targets) if (ima) { BKE_image_partial_update_mark_full_update(ima); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - BKE_image_free_gputextures(ima); - DEG_id_tag_update(&ima->id, 0); - } + BKE_image_free_gputextures(ima); + DEG_id_tag_update(&ima->id, 0); } } } @@ -321,7 +325,8 @@ static bool write_external_bake_pixels(const char *filepath, ImageFormatData *im_format, const bool is_noncolor, Mesh const *mesh_eval, - char const *uv_layer) + char const *uv_layer, + const float uv_offset[2]) { ImBuf *ibuf = NULL; bool ok = false; @@ -378,7 +383,7 @@ static bool write_external_bake_pixels(const char *filepath, mask_buffer = MEM_callocN(sizeof(char) * pixels_num, "Bake Mask"); RE_bake_mask_fill(pixel_array, pixels_num, mask_buffer); - RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer); + RE_bake_margin(ibuf, mask_buffer, margin, margin_type, mesh_eval, uv_layer, uv_offset); if (mask_buffer) { MEM_freeN(mask_buffer); @@ -443,7 +448,7 @@ 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 bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); + MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); if (mloopcol == NULL && !mcol_valid) { BKE_reportf(reports, RPT_ERROR, @@ -467,7 +472,6 @@ static bool bake_object_check(ViewLayer *view_layer, ED_object_get_active_image(ob, mat_nr, &image, NULL, &node, &ntree); if (image) { - ImBuf *ibuf; if (node) { if (BKE_node_is_connected_to_output(ntree, node)) { @@ -482,21 +486,27 @@ static bool bake_object_check(ViewLayer *view_layer, } } - void *lock; - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; - if (ibuf) { - BKE_image_release_ibuf(image, ibuf, lock); - } - else { - BKE_reportf(reports, - RPT_ERROR, - "Uninitialized image \"%s\" from object \"%s\"", - image->id.name + 2, - ob->id.name + 2); + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf) { + BKE_image_release_ibuf(image, ibuf, lock); + } + else { + BKE_reportf(reports, + RPT_ERROR, + "Uninitialized image \"%s\" from object \"%s\"", + image->id.name + 2, + ob->id.name + 2); - BKE_image_release_ibuf(image, ibuf, lock); - return false; + BKE_image_release_ibuf(image, ibuf, lock); + return false; + } } } else { @@ -687,36 +697,35 @@ static bool bake_targets_init_image_textures(const BakeAPIRender *bkr, } } - /* Over-allocate in case there is more materials than images. */ + /* Allocate material mapping. */ targets->materials_num = materials_num; - targets->images = MEM_callocN(sizeof(BakeImage) * targets->materials_num, "BakeTargets.images"); - targets->material_to_image = MEM_callocN(sizeof(int) * targets->materials_num, + targets->material_to_image = MEM_callocN(sizeof(Image *) * targets->materials_num, "BakeTargets.material_to_image"); /* Error handling and tag (in case multiple materials share the same image). */ BKE_main_id_tag_idcode(bkr->main, ID_IM, LIB_TAG_DOIT, false); + targets->images = NULL; + for (int i = 0; i < materials_num; i++) { Image *image; ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); - /* Some materials have no image, we just ignore those cases. */ - if (image == NULL) { - targets->material_to_image[i] = -1; - } - else if (image->id.tag & LIB_TAG_DOIT) { - for (int j = 0; j < i; j++) { - if (targets->images[j].image == image) { - targets->material_to_image[i] = j; - break; - } + targets->material_to_image[i] = image; + + /* Some materials have no image, we just ignore those cases. + * Also setup each image only once. */ + if (image && !(image->id.tag & LIB_TAG_DOIT)) { + LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) { + /* Add bake image. */ + targets->images = MEM_recallocN(targets->images, + sizeof(BakeImage) * (targets->images_num + 1)); + targets->images[targets->images_num].image = image; + targets->images[targets->images_num].tile_number = tile->tile_number; + targets->images_num++; } - } - else { - targets->material_to_image[i] = targets->images_num; - targets->images[targets->images_num].image = image; + image->id.tag |= LIB_TAG_DOIT; - targets->images_num++; } } @@ -735,13 +744,19 @@ static bool bake_targets_init_internal(const BakeAPIRender *bkr, /* Saving to image datablocks. */ for (int i = 0; i < targets->images_num; i++) { BakeImage *bk_image = &targets->images[i]; + + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = bk_image->tile_number; + void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(bk_image->image, &iuser, &lock); if (ibuf) { bk_image->width = ibuf->x; bk_image->height = ibuf->y; bk_image->offset = targets->pixels_num; + BKE_image_get_tile_uv(bk_image->image, bk_image->tile_number, bk_image->uv_offset); targets->pixels_num += (size_t)ibuf->x * (size_t)ibuf->y; } @@ -768,6 +783,7 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr, for (int i = 0; i < targets->images_num; i++) { BakeImage *bk_image = &targets->images[i]; const bool ok = write_internal_bake_pixels(bk_image->image, + bk_image->tile_number, pixel_array + bk_image->offset, targets->result + bk_image->offset * targets->channels_num, @@ -778,7 +794,8 @@ static bool bake_targets_output_internal(const BakeAPIRender *bkr, bkr->is_clear, targets->is_noncolor, mesh_eval, - bkr->uv_layer); + bkr->uv_layer, + bk_image->uv_offset); /* might be read by UI to set active image for display */ bake_update_image(bkr->area, bk_image->image); @@ -815,7 +832,6 @@ static bool bake_targets_init_external(const BakeAPIRender *bkr, bk_image->width = bkr->width; bk_image->height = bkr->height; bk_image->offset = targets->pixels_num; - bk_image->image = NULL; targets->pixels_num += (size_t)bkr->width * (size_t)bkr->height; @@ -827,7 +843,7 @@ static bool bake_targets_init_external(const BakeAPIRender *bkr, if (!bkr->is_split_materials) { /* saving a single image */ for (int i = 0; i < targets->materials_num; i++) { - targets->material_to_image[i] = 0; + targets->material_to_image[i] = targets->images[0].image; } } @@ -865,25 +881,26 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, } if (bkr->is_split_materials) { - if (bk_image->image) { - BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_"); + if (ob_eval->mat[i]) { + BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_"); + } + else if (mesh_eval->mat[i]) { + BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_"); } else { - if (ob_eval->mat[i]) { - BLI_path_suffix(name, FILE_MAX, ob_eval->mat[i]->id.name + 2, "_"); - } - else if (mesh_eval->mat[i]) { - BLI_path_suffix(name, FILE_MAX, mesh_eval->mat[i]->id.name + 2, "_"); - } - else { - /* if everything else fails, use the material index */ - char tmp[5]; - sprintf(tmp, "%d", i % 1000); - BLI_path_suffix(name, FILE_MAX, tmp, "_"); - } + /* if everything else fails, use the material index */ + char tmp[5]; + sprintf(tmp, "%d", i % 1000); + BLI_path_suffix(name, FILE_MAX, tmp, "_"); } } + if (bk_image->tile_number) { + char tmp[FILE_MAX]; + SNPRINTF(tmp, "%d", bk_image->tile_number); + BLI_path_suffix(name, FILE_MAX, tmp, "_"); + } + /* save it externally */ const bool ok = write_external_bake_pixels(name, pixel_array + bk_image->offset, @@ -896,7 +913,8 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, &bake->im_format, targets->is_noncolor, mesh_eval, - bkr->uv_layer); + bkr->uv_layer, + bk_image->uv_offset); if (!ok) { BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); @@ -927,7 +945,7 @@ 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 bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); + 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; @@ -1081,7 +1099,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); const bool mcol_valid = (mcol != NULL); - MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); + MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR); const int channels_num = targets->channels_num; const float *result = targets->result; diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 23d2327fe72..dfe858e5bd9 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -89,7 +89,7 @@ static void dt_add_vcol_layers(CustomData *cdata, EnumPropertyItem **r_item, int *r_totitem) { - int types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; + int types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; for (int i = 0; i < 2; i++) { CustomDataType type = types[i]; @@ -196,14 +196,14 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, cddata_masks.vmask |= CD_MASK_PROP_COLOR; } if (data_type & (DT_TYPE_MLOOPCOL_VERT)) { - cddata_masks.vmask |= CD_MASK_MLOOPCOL; + cddata_masks.vmask |= CD_MASK_PROP_BYTE_COLOR; } if (data_type & (DT_TYPE_MPROPCOL_LOOP)) { cddata_masks.lmask |= CD_MASK_PROP_COLOR; } if (data_type & (DT_TYPE_MLOOPCOL_LOOP)) { - cddata_masks.lmask |= CD_MASK_MLOOPCOL; + cddata_masks.lmask |= CD_MASK_PROP_BYTE_COLOR; } Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index f7543c97903..8b2dbd4a865 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -855,7 +855,7 @@ bool ED_object_modifier_apply(Main *bmain, Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); ModifierData *md_eval = (ob_eval) ? BKE_modifiers_findby_name(ob_eval, md->name) : md; - /* allow apply of a not-realtime modifier, by first re-enabling realtime. */ + /* Allow apply of a non-real-time modifier, by first re-enabling real-time. */ int prev_mode = md_eval->mode; md_eval->mode |= eModifierMode_Realtime; diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 966df0668ca..ba2efa6e517 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -40,6 +40,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_shrinkwrap.h" +#include "BKE_unit.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -282,7 +283,7 @@ static void voxel_size_parallel_lines_draw(uint pos3d, immEnd(); } -static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static void voxel_size_edit_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) { VoxelSizeEditCustomData *cd = static_cast<VoxelSizeEditCustomData *>(arg); @@ -338,8 +339,15 @@ static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), short fstyle_points = fstyle->points; char str[VOXEL_SIZE_EDIT_MAX_STR_LEN]; short strdrawlen = 0; - - BLI_snprintf(str, VOXEL_SIZE_EDIT_MAX_STR_LEN, "%.4f", cd->voxel_size); + Scene *scene = CTX_data_scene(C); + UnitSettings *unit = &scene->unit; + BKE_unit_value_as_string(str, + VOXEL_SIZE_EDIT_MAX_STR_LEN, + (double)(cd->voxel_size * unit->scale_length), + 4, + B_UNIT_LENGTH, + unit, + true); strdrawlen = BLI_strlen_utf8(str); immUnbindProgram(); @@ -885,9 +893,6 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); if (qj->smooth_normals) { - if (qj->use_mesh_symmetry) { - BKE_mesh_calc_normals(static_cast<Mesh *>(ob->data)); - } BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true); } diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc index 976fe683f95..da3df159c33 100644 --- a/source/blender/editors/object/object_transform.cc +++ b/source/blender/editors/object/object_transform.cc @@ -884,9 +884,6 @@ static int apply_objects_internal(bContext *C, /* adjust data */ BKE_mesh_transform(me, mat, true); - - /* If normal layers exist, they are now dirty. */ - BKE_mesh_normals_tag_dirty(me); } else if (ob->type == OB_ARMATURE) { bArmature *arm = static_cast<bArmature *>(ob->data); @@ -1173,6 +1170,12 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Apply Parent Inverse Operator + * \{ */ + static int object_parent_inverse_apply_exec(bContext *C, wmOperator *UNUSED(op)) { CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { @@ -1194,7 +1197,7 @@ void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot) { /* identifiers */ ot->name = "Apply Parent Inverse"; - ot->description = "Apply the object's parent inverse to the its data"; + ot->description = "Apply the object's parent inverse to its data"; ot->idname = "OBJECT_OT_parent_inverse_apply"; /* api callbacks */ diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index abdae5c44f9..08eed52f440 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -48,7 +48,7 @@ set(SRC paint_ops.c paint_stroke.c paint_utils.c - paint_vertex.c + paint_vertex.cc paint_vertex_color_ops.c paint_vertex_color_utils.c paint_vertex_proj.c diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 5f262384945..0d399419ad8 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -96,6 +96,7 @@ struct AddOperationExecutor { CurvesSculpt *curves_sculpt_ = nullptr; Brush *brush_ = nullptr; + BrushCurvesSculptSettings *brush_settings_ = nullptr; float brush_radius_re_; float2 brush_pos_re_; @@ -162,17 +163,18 @@ struct AddOperationExecutor { curves_sculpt_ = scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush(&curves_sculpt_->paint); + brush_settings_ = brush_->curves_sculpt_settings; brush_radius_re_ = BKE_brush_size_get(scene_, brush_); 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_->curves_sculpt_settings->add_amount); - interpolate_length_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; - interpolate_shape_ = curves_sculpt_->flag & CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE; + add_amount_ = std::max(0, brush_settings_->add_amount); + 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_; - new_curve_length_ = curves_sculpt_->curve_length; + new_curve_length_ = brush_settings_->curve_length; tot_old_curves_ = curves_->curves_num(); tot_old_points_ = curves_->points_num(); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 6a47f90d4ac..232d632aa3f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -136,6 +136,9 @@ struct CombOperationExecutor { curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; 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 7cc5e524c30..6228a643a76 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -311,6 +311,9 @@ struct CurvesEffectOperationExecutor { curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } CurvesSculpt &curves_sculpt = *scene_->toolsettings->curves_sculpt; brush_ = BKE_paint_brush(&curves_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 b367c5bb6ec..6d930d35f04 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -116,6 +116,9 @@ struct SnakeHookOperatorExecutor { curves_id_ = static_cast<Curves *>(object_->data); curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + if (curves_->curves_num() == 0) { + return; + } brush_pos_prev_re_ = self.last_mouse_position_re_; brush_pos_re_ = stroke_extension.mouse_position; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f784ebe4b4f..187f793eefe 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -133,7 +133,7 @@ void PAINT_OT_weight_gradient(struct wmOperatorType *ot); void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_vertex_paint(struct wmOperatorType *ot); -unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp, bool secondary); +unsigned int vpaint_get_current_color(struct Scene *scene, struct VPaint *vp, bool secondary); /* paint_vertex_color_utils.c */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 5929aa75b45..3ca686e9351 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -950,7 +950,6 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext) { SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)sgcontext->operation; Mesh *trim_mesh = trim_operation->mesh; - BKE_mesh_calc_normals(trim_mesh); const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(trim_mesh); BMesh *bm; @@ -1288,7 +1287,6 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) }), sculpt_mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); BKE_mesh_nomain_to_mesh( result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.cc index 33b92c22d3f..8b62dd33961 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -13,11 +13,14 @@ #include "MEM_guardedalloc.h" #include "BLI_array_utils.h" +#include "BLI_color.hh" +#include "BLI_color_mix.hh" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_task.h" +#include "BLI_task.hh" #include "DNA_brush_types.h" #include "DNA_mesh_types.h" @@ -26,6 +29,7 @@ #include "DNA_scene_types.h" #include "RNA_access.h" +#include "RNA_prototypes.h" #include "BKE_attribute.h" #include "BKE_brush.h" @@ -66,14 +70,53 @@ #include "paint_intern.h" /* own include */ #include "sculpt_intern.h" +using blender::IndexRange; +using namespace blender; +using namespace blender::color; + /* -------------------------------------------------------------------- */ /** \name Internal Utilities * \{ */ +static uint color2uint(ColorPaint4b c) +{ + return *(reinterpret_cast<uint *>(&c)); +} + +static bool isZero(ColorPaint4f c) +{ + return c.r == 0.0f && c.g == 0.0f && c.b == 0.0f && c.a == 0.0f; +} + +static bool isZero(ColorPaint4b c) +{ + return c.r == 0 && c.g == 0 && c.b == 0 && c.a == 0; +} + +template<typename Color = ColorPaint4f> static ColorPaint4f toFloat(const Color &c) +{ + if constexpr (std::is_same_v<Color, ColorPaint4b>) { + return c.decode(); + } + else { + return c; + } +} + +template<typename Color = ColorPaint4f> static Color fromFloat(const ColorPaint4f &c) +{ + if constexpr (std::is_same_v<Color, ColorPaint4b>) { + return c.encode(); + } + else { + return c; + } +} + /* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */ -struct VPaintAverageAccum { - uint len; - uint value[3]; +template<typename BlendType> struct VPaintAverageAccum { + BlendType len; + BlendType value[3]; }; struct WPaintAverageAccum { @@ -93,7 +136,27 @@ struct NormalAnglePrecalc { float angle_range; }; -static void view_angle_limits_init(struct NormalAnglePrecalc *a, float angle, bool do_mask_normal) +/* Returns number of elements. */ +static int get_vcol_elements(Mesh *me, size_t *r_elem_size) +{ + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + + if (r_elem_size) { + *r_elem_size = layer->type == CD_PROP_COLOR ? sizeof(float) * 4ULL : 4ULL; + } + + switch (domain) { + case ATTR_DOMAIN_POINT: + return me->totvert; + case ATTR_DOMAIN_CORNER: + return me->totloop; + default: + return 0; + } +} + +static void view_angle_limits_init(NormalAnglePrecalc *a, float angle, bool do_mask_normal) { angle = RAD2DEGF(angle); a->do_mask_normal = do_mask_normal; @@ -117,7 +180,7 @@ static void view_angle_limits_init(struct NormalAnglePrecalc *a, float angle, bo a->angle_inner__cos = cosf(a->angle_inner); } -static float view_angle_limits_apply_falloff(const struct NormalAnglePrecalc *a, +static float view_angle_limits_apply_falloff(const NormalAnglePrecalc *a, float angle_cos, float *mask_p) { @@ -169,10 +232,10 @@ static bool vertex_paint_use_fast_update_check(Object *ob) { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); - if (me_eval != NULL) { + if (me_eval != nullptr) { Mesh *me = BKE_mesh_from_object(ob); if (me && me->mloopcol) { - return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_MLOOPCOL)); + return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_PROP_BYTE_COLOR)); } } @@ -196,9 +259,8 @@ bool vertex_paint_mode_poll(bContext *C) } CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data); - AttributeDomain domain = BKE_id_attribute_domain((ID *)ob->data, layer); - return layer && layer->type == CD_MLOOPCOL && domain == ATTR_DOMAIN_CORNER; + return layer != nullptr; } static bool vertex_paint_poll_ex(bContext *C, bool check_tool) @@ -239,8 +301,8 @@ static bool weight_paint_poll_ex(bContext *C, bool check_tool) Object *ob = CTX_data_active_object(C); ScrArea *area; - if ((ob != NULL) && (ob->mode & OB_MODE_WEIGHT_PAINT) && - (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && + if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) && + (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) && (area = CTX_wm_area(C)) && (area->spacetype == SPACE_VIEW3D)) { ARegion *region = CTX_wm_region(C); if (ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) { @@ -262,41 +324,54 @@ bool weight_paint_poll_ignore_tool(bContext *C) return weight_paint_poll_ex(C, false); } -uint vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) +template<typename Color, typename Traits, AttributeDomain domain> +static Color vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) { Brush *brush = BKE_paint_brush(&vp->paint); - uchar col[4]; - rgb_float_to_uchar(col, - secondary ? BKE_brush_secondary_color_get(scene, brush) : - BKE_brush_color_get(scene, brush)); - col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ - return *(uint *)col; + float color[4]; + const float *brush_color = secondary ? BKE_brush_secondary_color_get(scene, brush) : + BKE_brush_color_get(scene, brush); + copy_v3_v3(color, brush_color); + color[3] = 1.0f; /* alpha isn't used, could even be removed to speedup paint a little */ + + return fromFloat<Color>(ColorPaint4f(color)); +} + +uint vpaint_get_current_color(Scene *scene, VPaint *vp, bool secondary) +{ + ColorPaint4b col = vpaint_get_current_col<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>( + scene, vp, secondary); + + return color2uint(col); } /* wpaint has 'wpaint_blend' */ -static uint vpaint_blend(const VPaint *vp, - uint color_curr, - uint color_orig, - uint color_paint, - const int alpha_i, - /* pre scaled from [0-1] --> [0-255] */ - const int brush_alpha_value_i) +template<typename Color, typename Traits> +static Color vpaint_blend(const VPaint *vp, + Color color_curr, + Color color_orig, + Color color_paint, + const typename Traits::ValueType alpha, + const typename Traits::BlendType brush_alpha_value) { + using Value = typename Traits::ValueType; + const Brush *brush = vp->paint.brush; - const IMB_BlendMode blend = brush->blend; + const IMB_BlendMode blend = (IMB_BlendMode)brush->blend; - uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); + Color color_blend = BLI_mix_colors<Color, Traits>(blend, color_curr, color_paint, alpha); /* If no accumulate, clip color adding with `color_orig` & `color_test`. */ if (!brush_use_accumulate(vp)) { - uint color_test, a; - char *cp, *ct, *co; + uint a; + Color color_test; + Value *cp, *ct, *co; - color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i); + color_test = BLI_mix_colors<Color, Traits>(blend, color_orig, color_paint, brush_alpha_value); - cp = (char *)&color_blend; - ct = (char *)&color_test; - co = (char *)&color_orig; + cp = (Value *)&color_blend; + ct = (Value *)&color_test; + co = (Value *)&color_orig; for (a = 0; a < 4; a++) { if (ct[a] < co[a]) { @@ -320,36 +395,15 @@ static uint vpaint_blend(const VPaint *vp, if ((brush->flag & BRUSH_LOCK_ALPHA) && !ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) { - char *cp, *cc; - cp = (char *)&color_blend; - cc = (char *)&color_curr; + Value *cp, *cc; + cp = (Value *)&color_blend; + cc = (Value *)&color_curr; cp[3] = cc[3]; } return color_blend; } -static void tex_color_alpha(VPaint *vp, const ViewContext *vc, const float co[3], float r_rgba[4]) -{ - const Brush *brush = BKE_paint_brush(&vp->paint); - BLI_assert(brush->mtex.tex != NULL); - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL); - } - else { - float co_ss[2]; /* screenspace */ - if (ED_view3d_project_float_object( - vc->region, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == - V3D_PROJ_RET_OK) { - const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ - BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL); - } - else { - zero_v4(r_rgba); - } - } -} - /* vpaint has 'vpaint_blend' */ static float wpaint_blend(const VPaint *wp, float weight, @@ -359,7 +413,7 @@ static float wpaint_blend(const VPaint *wp, const short do_flip) { const Brush *brush = wp->paint.brush; - IMB_BlendMode blend = brush->blend; + IMB_BlendMode blend = (IMB_BlendMode)brush->blend; if (do_flip) { switch (blend) { @@ -390,6 +444,32 @@ static float wpaint_blend(const VPaint *wp, return weight; } +static void paint_and_tex_color_alpha_intern(VPaint *vp, + const ViewContext *vc, + const float co[3], + float r_rgba[4]) +{ + const Brush *brush = BKE_paint_brush(&vp->paint); + BLI_assert(brush->mtex.tex != nullptr); + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, nullptr); + } + else { + float co_ss[2]; /* screenspace */ + if (ED_view3d_project_float_object( + vc->region, + co, + co_ss, + (eV3DProjTest)(V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR)) == V3D_PROJ_RET_OK) { + const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ + BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, nullptr); + } + else { + zero_v4(r_rgba); + } + } +} + static float wpaint_clamp_monotonic(float oldval, float curval, float newval) { if (newval < oldval) { @@ -488,7 +568,7 @@ static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert, uint i, tot = 0; MDeformWeight *dw; - if (lock_flags == NULL) { + if (lock_flags == nullptr) { do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap); return true; } @@ -703,7 +783,7 @@ struct WeightPaintGroupData { * this _could_ be made a part of the operators 'WPaintData' struct, or at * least a member, but for now keep its own struct, initialized on every * paint stroke update - campbell */ -typedef struct WeightPaintInfo { +struct WeightPaintInfo { int defbase_tot; @@ -711,7 +791,7 @@ typedef struct WeightPaintInfo { int defbase_tot_sel; int defbase_tot_unsel; - struct WeightPaintGroupData active, mirror; + WeightPaintGroupData active, mirror; /* boolean array for locked bones, * length of defbase_tot */ @@ -734,7 +814,7 @@ typedef struct WeightPaintInfo { bool is_normalized; float brush_alpha_value; /* result of BKE_brush_alpha_get() */ -} WeightPaintInfo; +}; static void do_weight_paint_vertex_single( /* vars which remain the same for every vert */ @@ -746,7 +826,7 @@ static void do_weight_paint_vertex_single( float alpha, float paintweight) { - Mesh *me = ob->data; + Mesh *me = (Mesh *)ob->data; MDeformVert *dv = &me->dvert[index]; bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; @@ -763,7 +843,7 @@ static void do_weight_paint_vertex_single( /* Check if we should mirror vertex groups (X-axis). */ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, topology); vgroup_mirr = wpi->mirror.index; /* another possible error - mirror group _and_ active group are the same (which is fine), @@ -801,7 +881,7 @@ static void do_weight_paint_vertex_single( dw = BKE_defvert_ensure_index(dv, wpi->active.index); } - if (dw == NULL) { + if (dw == nullptr) { return; } @@ -811,9 +891,9 @@ static void do_weight_paint_vertex_single( if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr); - if (dw_mirr == NULL) { + if (dw_mirr == nullptr) { index_mirr = vgroup_mirr = -1; - dv_mirr = NULL; + dv_mirr = nullptr; } } else { @@ -834,8 +914,8 @@ static void do_weight_paint_vertex_single( } } else { - dv_mirr = NULL; - dw_mirr = NULL; + dv_mirr = nullptr; + dw_mirr = nullptr; } weight_cur = dw->weight; @@ -959,13 +1039,13 @@ static void do_weight_paint_vertex_multi( float alpha, float paintweight) { - Mesh *me = ob->data; + Mesh *me = (Mesh *)ob->data; MDeformVert *dv = &me->dvert[index]; bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; /* mirror vars */ int index_mirr = -1; - MDeformVert *dv_mirr = NULL; + MDeformVert *dv_mirr = nullptr; /* weights */ float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr; @@ -973,7 +1053,7 @@ static void do_weight_paint_vertex_multi( /* Check if we should mirror vertex groups (X-axis). */ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, topology); if (!ELEM(index_mirr, -1, index)) { dv_mirr = &me->dvert[index_mirr]; @@ -1033,13 +1113,13 @@ static void do_weight_paint_vertex_multi( /* verify for all groups that 0 < result <= 1 */ multipaint_clamp_change(dv, wpi->defbase_tot, wpi->defbase_sel, &change); - if (dv_mirr != NULL) { + if (dv_mirr != nullptr) { curw_mirr = BKE_defvert_multipaint_collective_weight( dv_mirr, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->is_normalized); if (curw_mirr == 0.0f) { /* can't mirror into a zero weight vertex */ - dv_mirr = NULL; + dv_mirr = nullptr; } else { /* mirror is changed to achieve the same collective weight value */ @@ -1062,7 +1142,7 @@ static void do_weight_paint_vertex_multi( /* apply validated change to vertex and mirror */ multipaint_apply_change(dv, wpi->defbase_tot, change, wpi->defbase_sel); - if (dv_mirr != NULL) { + if (dv_mirr != nullptr) { multipaint_apply_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel); } @@ -1071,7 +1151,7 @@ static void do_weight_paint_vertex_multi( do_weight_paint_normalize_all_locked_try_active( dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - if (dv_mirr != NULL) { + if (dv_mirr != nullptr) { do_weight_paint_normalize_all_locked_try_active( dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); } @@ -1105,21 +1185,46 @@ static void vertex_paint_init_session(Depsgraph *depsgraph, /* Create persistent sculpt mode data */ BKE_sculpt_toolsettings_data_ensure(scene); - BLI_assert(ob->sculpt == NULL); - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + BLI_assert(ob->sculpt == nullptr); + ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session"); ob->sculpt->mode_type = object_mode; - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); } -static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob) +static void vertex_paint_init_stroke(Scene *scene, Depsgraph *depsgraph, Object *ob) { - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + ToolSettings *ts = scene->toolsettings; + SculptSession *ss = ob->sculpt; + + /* Ensure ss->cache is allocated. It will mostly be initialized in + * vwpaint_update_cache_invariants and vwpaint_update_cache_variants. + */ + if (!ss->cache) { + ss->cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache"); + } + + /* Allocate scratch array for previous colors if needed. */ + if (!brush_use_accumulate(ts->vpaint)) { + if (!ob->sculpt->cache->prev_colors_vpaint) { + Mesh *me = BKE_object_get_original_mesh(ob); + size_t elem_size; + int elem_num; + + elem_num = get_vcol_elements(me, &elem_size); + + ob->sculpt->cache->prev_colors_vpaint = (uint *)MEM_callocN(elem_num * elem_size, __func__); + } + } + else { + MEM_SAFE_FREE(ob->sculpt->cache->prev_colors_vpaint); + } } static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) { /* Create maps */ - struct SculptVertexPaintGeomMap *gmap = NULL; + SculptVertexPaintGeomMap *gmap = nullptr; if (ob->mode == OB_MODE_VERTEX_PAINT) { gmap = &ob->sculpt->mode.vpaint.gmap; BLI_assert(ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT); @@ -1129,18 +1234,18 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT); } else { - ob->sculpt->mode_type = 0; + ob->sculpt->mode_type = (eObjectMode)0; BLI_assert(0); return; } - Mesh *me = ob->data; + Mesh *me = (Mesh *)ob->data; - if (gmap->vert_to_loop == NULL) { - gmap->vert_map_mem = NULL; - gmap->vert_to_loop = NULL; - gmap->poly_map_mem = NULL; - gmap->vert_to_poly = NULL; + if (gmap->vert_to_loop == nullptr) { + gmap->vert_map_mem = nullptr; + gmap->vert_to_loop = nullptr; + gmap->poly_map_mem = nullptr; + gmap->vert_to_poly = nullptr; BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, &gmap->vert_map_mem, me->mpoly, @@ -1158,24 +1263,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) } /* Create average brush arrays */ - if (ob->mode == OB_MODE_VERTEX_PAINT) { - if (!brush_use_accumulate(ts->vpaint)) { - if (ob->sculpt->mode.vpaint.previous_color == NULL) { - ob->sculpt->mode.vpaint.previous_color = MEM_callocN(me->totloop * sizeof(uint), __func__); - } - } - else { - MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); - } - } - else if (ob->mode == OB_MODE_WEIGHT_PAINT) { + if (ob->mode == OB_MODE_WEIGHT_PAINT) { if (!brush_use_accumulate(ts->wpaint)) { - if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { - ob->sculpt->mode.wpaint.alpha_weight = MEM_callocN(me->totvert * sizeof(float), __func__); + if (ob->sculpt->mode.wpaint.alpha_weight == nullptr) { + ob->sculpt->mode.wpaint.alpha_weight = (float *)MEM_callocN(me->totvert * sizeof(float), + __func__); } - if (ob->sculpt->mode.wpaint.dvert_prev == NULL) { - ob->sculpt->mode.wpaint.dvert_prev = MEM_callocN(me->totvert * sizeof(MDeformVert), - __func__); + if (ob->sculpt->mode.wpaint.dvert_prev == nullptr) { + ob->sculpt->mode.wpaint.dvert_prev = (MDeformVert *)MEM_callocN( + me->totvert * sizeof(MDeformVert), __func__); MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; for (int i = 0; i < me->totvert; i++, dv++) { /* Use to show this isn't initialized, never apply to the mesh data. */ @@ -1185,10 +1281,10 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) } else { MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight); - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + if (ob->sculpt->mode.wpaint.dvert_prev != nullptr) { BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert); MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev); - ob->sculpt->mode.wpaint.dvert_prev = NULL; + ob->sculpt->mode.wpaint.dvert_prev = nullptr; } } } @@ -1213,7 +1309,7 @@ static void ed_vwpaintmode_enter_generic( if (mode_flag == OB_MODE_VERTEX_PAINT) { const ePaintMode paint_mode = PAINT_MODE_VERTEX; - ED_mesh_color_ensure(me, NULL); + ED_mesh_color_ensure(me, nullptr); BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); @@ -1240,7 +1336,7 @@ static void ed_vwpaintmode_enter_generic( if (ob->sculpt) { if (ob->sculpt->cache) { SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } BKE_sculptsession_free(ob); } @@ -1255,7 +1351,7 @@ void ED_object_vpaintmode_enter_ex(Main *bmain, Depsgraph *depsgraph, Scene *sce { ed_vwpaintmode_enter_generic(bmain, depsgraph, scene, ob, OB_MODE_VERTEX_PAINT); } -void ED_object_vpaintmode_enter(struct bContext *C, Depsgraph *depsgraph) +void ED_object_vpaintmode_enter(bContext *C, Depsgraph *depsgraph) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -1267,7 +1363,7 @@ void ED_object_wpaintmode_enter_ex(Main *bmain, Depsgraph *depsgraph, Scene *sce { ed_vwpaintmode_enter_generic(bmain, depsgraph, scene, ob, OB_MODE_WEIGHT_PAINT); } -void ED_object_wpaintmode_enter(struct bContext *C, Depsgraph *depsgraph) +void ED_object_wpaintmode_enter(bContext *C, Depsgraph *depsgraph) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -1309,7 +1405,7 @@ static void ed_vwpaintmode_exit_generic(Object *ob, const eObjectMode mode_flag) /* If the cache is not released by a cancel or a done, free it now. */ if (ob->sculpt && ob->sculpt->cache) { SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } BKE_sculptsession_free(ob); @@ -1332,7 +1428,7 @@ void ED_object_vpaintmode_exit_ex(Object *ob) { ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT); } -void ED_object_vpaintmode_exit(struct bContext *C) +void ED_object_vpaintmode_exit(bContext *C) { Object *ob = CTX_data_active_object(C); ED_object_vpaintmode_exit_ex(ob); @@ -1342,7 +1438,7 @@ void ED_object_wpaintmode_exit_ex(Object *ob) { ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT); } -void ED_object_wpaintmode_exit(struct bContext *C) +void ED_object_wpaintmode_exit(bContext *C) { Object *ob = CTX_data_active_object(C); ED_object_wpaintmode_exit_ex(ob); @@ -1368,7 +1464,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op) ToolSettings *ts = scene->toolsettings; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, ob, (eObjectMode)mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } @@ -1409,10 +1505,10 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op) static bool paint_mode_toggle_poll_test(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) { + if (ob == nullptr || ob->type != OB_MESH) { return false; } - if (!ob->data || ID_IS_LINKED(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob->data)) { + if (!ob->data || ID_IS_LINKED(ob->data)) { return false; } return true; @@ -1442,9 +1538,9 @@ void PAINT_OT_weight_paint_toggle(wmOperatorType *ot) struct WPaintData { ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + NormalAnglePrecalc normal_angle_precalc; - struct WeightPaintGroupData active, mirror; + WeightPaintGroupData active, mirror; /* variables for auto normalize */ const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */ @@ -1507,7 +1603,7 @@ static void vwpaint_update_cache_invariants( StrokeCache *cache; Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - ViewContext *vc = paint_stroke_view_context(op->customdata); + ViewContext *vc = paint_stroke_view_context((PaintStroke *)op->customdata); Object *ob = CTX_data_active_object(C); float mat[3][3]; float view_dir[3] = {0.0f, 0.0f, 1.0f}; @@ -1515,7 +1611,7 @@ static void vwpaint_update_cache_invariants( /* VW paint needs to allocate stroke cache before update is called. */ if (!ss->cache) { - cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); + cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache"); ss->cache = cache; } else { @@ -1551,7 +1647,7 @@ static void vwpaint_update_cache_invariants( /* Truly temporary data that isn't stored in properties */ cache->vc = vc; cache->brush = brush; - cache->first_time = 1; + cache->first_time = true; /* cache projection matrix */ ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); @@ -1618,12 +1714,12 @@ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, P static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { Scene *scene = CTX_data_scene(C); - struct PaintStroke *stroke = op->customdata; + PaintStroke *stroke = (PaintStroke *)op->customdata; ToolSettings *ts = scene->toolsettings; Object *ob = CTX_data_active_object(C); Mesh *me = BKE_mesh_from_object(ob); - struct WPaintData *wpd; - struct WPaintVGroupIndex vgroup_index; + WPaintData *wpd; + WPaintVGroupIndex vgroup_index; int defbase_tot, defbase_tot_sel; bool *defbase_sel; SculptSession *ss = ob->sculpt; @@ -1638,13 +1734,13 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* check if we are attempting to paint onto a locked vertex group, * and other options disallow it from doing anything useful */ bDeformGroup *dg; - dg = BLI_findlink(&me->vertex_group_names, vgroup_index.active); + dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, vgroup_index.active); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); return false; } if (vgroup_index.mirror != -1) { - dg = BLI_findlink(&me->vertex_group_names, vgroup_index.mirror); + dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, vgroup_index.mirror); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); return false; @@ -1667,7 +1763,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo for (i = 0; i < defbase_tot; i++) { if (defbase_sel[i]) { - dg = BLI_findlink(&me->vertex_group_names, i); + dg = (bDeformGroup *)BLI_findlink(&me->vertex_group_names, i); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); MEM_freeN(defbase_sel); @@ -1679,7 +1775,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* ALLOCATIONS! no return after this line */ /* make mode data storage */ - wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); + wpd = (WPaintData *)MEM_callocN(sizeof(WPaintData), "WPaintData"); paint_stroke_set_mode_data(stroke, wpd); ED_view3d_viewcontext_init(C, &wpd->vc, depsgraph); view_angle_limits_init(&wpd->normal_angle_precalc, @@ -1712,10 +1808,10 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo } if (wpd->do_lock_relative || (ts->auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) { - bool *unlocked = MEM_dupallocN(wpd->vgroup_validmap); + bool *unlocked = (bool *)MEM_dupallocN(wpd->vgroup_validmap); if (wpd->lock_flags) { - bool *locked = MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__); + bool *locked = (bool *)MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__); BKE_object_defgroup_split_locked_validmap( wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked); wpd->vgroup_locked = locked; @@ -1726,7 +1822,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo if (wpd->do_multipaint && ts->auto_normalize) { bool *tmpflags; - tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__); + tmpflags = (bool *)MEM_mallocN(sizeof(bool) * defbase_tot, __func__); if (wpd->lock_flags) { BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot); } @@ -1738,27 +1834,27 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo else if (ts->auto_normalize) { bool *tmpflags; - tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) : + (bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__); tmpflags[wpd->active.index] = true; wpd->active.lock = tmpflags; - tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) : + (bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__); tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true; wpd->mirror.lock = tmpflags; } /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, ob); + vertex_paint_init_stroke(scene, depsgraph, ob); vwpaint_update_cache_invariants(C, vp, ss, op, mouse); vertex_paint_init_session_data(ts, ob); if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { - wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__); + wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * me->totvert, __func__); } - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + if (ob->sculpt->mode.wpaint.dvert_prev != nullptr) { MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; for (int i = 0; i < me->totvert; i++, dv++) { /* Use to show this isn't initialized, never apply to the mesh data. */ @@ -1807,27 +1903,26 @@ static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; const MDeformVert *dv = &data->me->dvert[n]; data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); } static void precompute_weight_values( - bContext *C, Object *ob, Brush *brush, struct WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) + bContext *C, Object *ob, Brush *brush, WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) { if (wpd->precomputed_weight_ready && !brush_use_accumulate_ex(brush, ob->mode)) { return; } /* threaded loop over vertices */ - SculptThreadedTaskData data = { - .C = C, - .ob = ob, - .wpd = wpd, - .wpi = wpi, - .me = me, - }; + SculptThreadedTaskData data; + data.C = C; + data.ob = ob; + data.wpd = wpd; + data.wpi = wpi; + data.me = me; TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -1840,11 +1935,11 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const Brush *brush = data->brush; const StrokeCache *cache = ss->cache; @@ -1931,11 +2026,11 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + const SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; const Brush *brush = data->brush; const Scene *scene = CTX_data_scene(data->C); @@ -2041,7 +2136,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const bool has_grids = (pbvh_type == PBVH_GRIDS); @@ -2112,7 +2207,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, static void do_wpaint_brush_calc_average_weight_cb_ex( void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; SculptSession *ss = data->ob->sculpt; StrokeCache *cache = ss->cache; const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); @@ -2122,7 +2217,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n; + WPaintAverageAccum *accum = (WPaintAverageAccum *)data->custom_data + n; accum->len = 0; accum->value = 0.0; @@ -2143,7 +2238,6 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - // const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; const char v_flag = data->me->mvert[v_index].flag; /* If the vertex is selected. */ @@ -2162,7 +2256,8 @@ static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode) { - struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); + WPaintAverageAccum *accum = (WPaintAverageAccum *)MEM_mallocN(sizeof(*accum) * totnode, + __func__); data->custom_data = accum; TaskParallelSettings settings; @@ -2187,7 +2282,7 @@ static void wpaint_paint_leaves(bContext *C, Object *ob, Sculpt *sd, VPaint *vp, - struct WPaintData *wpd, + WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me, PBVHNode **nodes, @@ -2197,17 +2292,16 @@ static void wpaint_paint_leaves(bContext *C, const Brush *brush = ob->sculpt->cache->brush; /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .C = C, - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .vp = vp, - .wpd = wpd, - .wpi = wpi, - .me = me, - }; + SculptThreadedTaskData data = {nullptr}; + data.C = C; + data.sd = sd; + data.ob = ob; + data.brush = brush; + data.nodes = nodes; + data.vp = vp; + data.wpd = wpd; + data.wpi = wpi; + data.me = me; /* Use this so average can modify its weight without touching the brush. */ data.strength = BKE_brush_weight_get(scene, brush); @@ -2239,16 +2333,16 @@ static PBVHNode **vwpaint_pbvh_gather_generic( { SculptSession *ss = ob->sculpt; const bool use_normal = vwpaint_use_normal(wp); - PBVHNode **nodes = NULL; + PBVHNode **nodes = nullptr; /* Build a list of all nodes that are potentially within the brush's area of influence */ if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - }; + SculptSearchSphereData data = {nullptr}; + data.ss = ss; + data.sd = sd; + data.radius_squared = ss->cache->radius_squared; + data.original = true; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); if (use_normal) { SCULPT_pbvh_calc_area_normal( @@ -2259,16 +2353,16 @@ static PBVHNode **vwpaint_pbvh_gather_generic( } } else { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + DistRayAABB_Precalc dist_ray_to_aabb_precalc; dist_squared_ray_to_aabb_v3_precalc( &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - }; + SculptSearchCircleData data = {nullptr}; + data.ss = ss; + data.sd = sd; + data.radius_squared = ss->cache->radius_squared; + data.original = true; + data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc; + BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode); if (use_normal) { copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal); @@ -2284,7 +2378,7 @@ static void wpaint_do_paint(bContext *C, Object *ob, VPaint *wp, Sculpt *sd, - struct WPaintData *wpd, + WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me, Brush *brush, @@ -2311,7 +2405,7 @@ static void wpaint_do_radial_symmetry(bContext *C, Object *ob, VPaint *wp, Sculpt *sd, - struct WPaintData *wpd, + WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me, Brush *brush, @@ -2327,10 +2421,10 @@ static void wpaint_do_radial_symmetry(bContext *C, /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */ static void wpaint_do_symmetrical_brush_actions( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi) + bContext *C, Object *ob, VPaint *wp, Sculpt *sd, WPaintData *wpd, WeightPaintInfo *wpi) { Brush *brush = BKE_paint_brush(&wp->paint); - Mesh *me = ob->data; + Mesh *me = (Mesh *)ob->data; SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -2380,14 +2474,14 @@ static void wpaint_do_symmetrical_brush_actions( static void wpaint_stroke_update_step(bContext *C, wmOperator *UNUSED(op), - struct PaintStroke *stroke, + PaintStroke *stroke, PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); VPaint *wp = ts->wpaint; Brush *brush = BKE_paint_brush(&wp->paint); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); + WPaintData *wpd = (WPaintData *)paint_stroke_mode_data(stroke); ViewContext *vc; Object *ob = CTX_data_active_object(C); @@ -2400,11 +2494,11 @@ static void wpaint_stroke_update_step(bContext *C, const float brush_alpha_value = BKE_brush_alpha_get(scene, brush); - /* intentionally don't initialize as NULL, make sure we initialize all members below */ + /* intentionally don't initialize as nullptr, make sure we initialize all members below */ WeightPaintInfo wpi; /* cannot paint if there is no stroke data */ - if (wpd == NULL) { + if (wpd == nullptr) { /* XXX: force a redraw here, since even though we can't paint, * at least view won't freeze until stroke ends */ ED_region_tag_redraw(CTX_wm_region(C)); @@ -2434,7 +2528,7 @@ static void wpaint_stroke_update_step(bContext *C, wpi.vgroup_unlocked = wpd->vgroup_unlocked; wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip") || ss->cache->invert; wpi.do_multipaint = wpd->do_multipaint; - wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL) && + wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != nullptr) && (wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index])); wpi.do_lock_relative = wpd->do_lock_relative; wpi.is_normalized = wpi.do_auto_normalize || wpi.do_lock_relative; @@ -2442,7 +2536,7 @@ static void wpaint_stroke_update_step(bContext *C, /* *** done setting up WeightPaintInfo *** */ if (wpd->precomputed_weight) { - precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data); + precompute_weight_values(C, ob, brush, wpd, &wpi, (Mesh *)ob->data); } wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); @@ -2455,9 +2549,9 @@ static void wpaint_stroke_update_step(bContext *C, mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location); paint_last_stroke_update(scene, loc_world); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update(ob->data, 0); + DEG_id_tag_update((ID *)ob->data, 0); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); swap_m4m4(wpd->vc.rv3d->persmat, mat); @@ -2483,10 +2577,10 @@ static void wpaint_stroke_update_step(bContext *C, ED_region_tag_redraw_partial(vc->region, &r, true); } -static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) +static void wpaint_stroke_done(const bContext *C, PaintStroke *stroke) { Object *ob = CTX_data_active_object(C); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); + WPaintData *wpd = (WPaintData *)paint_stroke_mode_data(stroke); if (wpd) { if (wpd->defbase_sel) { @@ -2530,7 +2624,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) ParticleSystem *psys; int i; - for (psys = ob->particlesystem.first; psys; psys = psys->next) { + for (psys = (ParticleSystem *)ob->particlesystem.first; psys; psys = psys->next) { for (i = 0; i < PSYS_TOT_VG; i++) { if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(ob)) { psys->recalc |= ID_RECALC_PSYS_RESET; @@ -2540,12 +2634,12 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) } } - DEG_id_tag_update(ob->data, 0); + DEG_id_tag_update((ID *)ob->data, 0); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -2557,12 +2651,12 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) SCULPT_stroke_get_location, wpaint_stroke_test_start, wpaint_stroke_update_step, - NULL, + nullptr, wpaint_stroke_done, event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op, op->customdata); + paint_stroke_free(C, op, (PaintStroke *)op->customdata); return OPERATOR_FINISHED; } /* add modal handler */ @@ -2581,12 +2675,12 @@ static int wpaint_exec(bContext *C, wmOperator *op) SCULPT_stroke_get_location, wpaint_stroke_test_start, wpaint_stroke_update_step, - NULL, + nullptr, wpaint_stroke_done, 0); /* frees op->customdata */ - paint_stroke_exec(C, op, op->customdata); + paint_stroke_exec(C, op, (PaintStroke *)op->customdata); return OPERATOR_FINISHED; } @@ -2596,15 +2690,15 @@ static void wpaint_cancel(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if (ob->sculpt->cache) { SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } - paint_stroke_cancel(C, op, op->customdata); + paint_stroke_cancel(C, op, (PaintStroke *)op->customdata); } static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) { - return paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); + return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata); } void PAINT_OT_weight_paint(wmOperatorType *ot) @@ -2647,7 +2741,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) ToolSettings *ts = scene->toolsettings; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, ob, (eObjectMode)mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } @@ -2667,13 +2761,12 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); } - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); /* update modifier stack for mapping requirements */ DEG_id_tag_update(&me->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); WM_toolsystem_update_from_context_view3d(C); @@ -2720,14 +2813,20 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) * - revise whether op->customdata should be added in object, in set_vpaint. */ -struct VPaintData { +struct VPaintDataBase { ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + AttributeDomain domain; + CustomDataType type; +}; + +template<typename Color, typename Traits, AttributeDomain domain> +struct VPaintData : public VPaintDataBase { + NormalAnglePrecalc normal_angle_precalc; - uint paintcol; + Color paintcol; struct VertProjHandle *vp_handle; - struct CoNo *vertexcosnos; + CoNo *vertexcosnos; /** * Modify #Mesh.mloopcol directly, since the derived mesh is drawing from this @@ -2743,44 +2842,43 @@ struct VPaintData { /* Special storage for smear brush, avoid feedback loop - update each step. */ struct { - uint *color_prev; - uint *color_curr; + void *color_prev; + void *color_curr; } smear; }; -static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) +template<typename Color, typename Traits, AttributeDomain domain> +static void *vpaint_init_vpaint(bContext *C, + wmOperator *op, + Scene *scene, + Depsgraph *depsgraph, + VPaint *vp, + Object *ob, + Mesh *me, + const Brush *brush) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - struct PaintStroke *stroke = op->customdata; - VPaint *vp = ts->vpaint; - Brush *brush = BKE_paint_brush(&vp->paint); - struct VPaintData *vpd; - Object *ob = CTX_data_active_object(C); - Mesh *me; - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + VPaintData<Color, Traits, domain> *vpd; - /* context checks could be a poll() */ - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) { - return false; - } + size_t elem_size; + int elem_num = get_vcol_elements(me, &elem_size); + /* make mode data storage */ + vpd = MEM_new<VPaintData<Color, Traits, domain>>("VPaintData"); - ED_mesh_color_ensure(me, NULL); - if (me->mloopcol == NULL) { - return false; + if constexpr (std::is_same_v<Color, ColorPaint4f>) { + vpd->type = CD_PROP_COLOR; + } + else { + vpd->type = CD_PROP_BYTE_COLOR; } - /* make mode data storage */ - vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); - paint_stroke_set_mode_data(stroke, vpd); + vpd->domain = domain; + ED_view3d_viewcontext_init(C, &vpd->vc, depsgraph); view_angle_limits_init(&vpd->normal_angle_precalc, vp->paint.brush->falloff_angle, (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); - vpd->paintcol = vpaint_get_current_col( + vpd->paintcol = vpaint_get_current_col<Color, Traits, domain>( scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; @@ -2789,22 +2887,23 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f * if not we can skip face map trickiness */ if (vertex_paint_use_fast_update_check(ob)) { vpd->use_fast_update = true; - // printf("Fast update!\n"); } else { vpd->use_fast_update = false; - // printf("No fast update!\n"); } /* to keep tracked of modified loops for shared vertex color blending */ if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { - vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); + vpd->mlooptag = (bool *)MEM_mallocN(sizeof(bool) * elem_num, "VPaintData mlooptag"); } if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { - vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__); - memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop); - vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + + vpd->smear.color_prev = MEM_malloc_arrayN(elem_num, elem_size, __func__); + memcpy(vpd->smear.color_prev, layer->data, elem_size * elem_num); + + vpd->smear.color_curr = (uint *)MEM_dupallocN(vpd->smear.color_prev); } /* Create projection handle */ @@ -2814,516 +2913,845 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f ob->sculpt->building_vp_handle = false; } + return static_cast<void *>(vpd); +} + +static bool vpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + PaintStroke *stroke = (PaintStroke *)op->customdata; + VPaint *vp = ts->vpaint; + Brush *brush = BKE_paint_brush(&vp->paint); + Object *ob = CTX_data_active_object(C); + Mesh *me; + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + /* context checks could be a poll() */ + me = BKE_mesh_from_object(ob); + if (me == nullptr || me->totpoly == 0) { + return false; + } + + ED_mesh_color_ensure(me, nullptr); + + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + if (!layer) { + return false; + } + + AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + void *vpd = nullptr; + + if (domain == ATTR_DOMAIN_POINT) { + if (layer->type == CD_PROP_COLOR) { + vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>( + C, op, scene, depsgraph, vp, ob, me, brush); + } + else if (layer->type == CD_PROP_BYTE_COLOR) { + vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>( + C, op, scene, depsgraph, vp, ob, me, brush); + } + } + else if (domain == ATTR_DOMAIN_CORNER) { + if (layer->type == CD_PROP_COLOR) { + vpd = vpaint_init_vpaint<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>( + C, op, scene, depsgraph, vp, ob, me, brush); + } + else if (layer->type == CD_PROP_BYTE_COLOR) { + vpd = vpaint_init_vpaint<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>( + C, op, scene, depsgraph, vp, ob, me, brush); + } + } + + BLI_assert(vpd != nullptr); + + paint_stroke_set_mode_data(stroke, vpd); + /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, ob); + vertex_paint_init_stroke(scene, depsgraph, ob); vwpaint_update_cache_invariants(C, vp, ss, op, mouse); vertex_paint_init_session_data(ts, ob); - if (ob->sculpt->mode.vpaint.previous_color != NULL) { - memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop); - } - return true; } -static void do_vpaint_brush_calc_average_color_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) +template<class Color = ColorPaint4b, typename Traits = ByteTraits> +static void do_vpaint_brush_blur_loops(bContext *C, + Sculpt *UNUSED(sd), + VPaint *vp, + VPaintData<Color, Traits, ATTR_DOMAIN_CORNER> *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode, + Color *lcol) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + using Blend = typename Traits::BlendType; - StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - char *col; - const bool use_vert_sel = (data->me->editflag & - (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + SculptSession *ss = ob->sculpt; - struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; - accum->len = 0; - memset(accum->value, 0, sizeof(accum->value)); + const Brush *brush = ob->sculpt->cache->brush; + const Scene *scene = CTX_data_scene(C); + + Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { + for (int n : range) { + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const StrokeCache *cache = ss->cache; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(vp); + const bool use_vert_sel = (me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( + ss, brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_v3v3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + /* Get the average poly color */ + Color color_final(0, 0, 0, 0); - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; - if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) { - /* If the vertex is selected for painting. */ - const MVert *mv = &data->me->mvert[v_index]; - if (!use_vert_sel || mv->flag & SELECT) { - accum->len += gmap->vert_to_loop[v_index].count; - /* if a vertex is within the brush region, then add its color to the blend. */ - for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - col = (char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - accum->value[0] += col[0] * col[0]; - accum->value[1] += col[1] * col[1]; - accum->value[2] += col[2] * col[2]; + int total_hit_loops = 0; + Blend blend[4] = {0}; + + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const uint l_index = mp->loopstart + k; + Color *col = lcol + l_index; + + /* Color is squared to compensate the sqrt color encoding. */ + blend[0] += (Blend)col->r * (Blend)col->r; + blend[1] += (Blend)col->g * (Blend)col->g; + blend[2] += (Blend)col->b * (Blend)col->b; + blend[3] += (Blend)col->a * (Blend)col->a; + } + } + } + + if (total_hit_loops != 0) { + /* Use rgb^2 color averaging. */ + Color *col = &color_final; + + color_final.r = Traits::round( + sqrtf(Traits::divide_round(blend[0], total_hit_loops))); + color_final.g = Traits::round( + sqrtf(Traits::divide_round(blend[1], total_hit_loops))); + color_final.b = Traits::round( + sqrtf(Traits::divide_round(blend[2], total_hit_loops))); + color_final.a = Traits::round( + sqrtf(Traits::divide_round(blend[3], total_hit_loops))); + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(me->mloop[l_index].v == v_index); + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ + + if (previous_color != nullptr) { + /* Get the previous loop color */ + if (isZero(previous_color[l_index])) { + previous_color[l_index] = lcol[l_index]; + } + color_orig = previous_color[l_index]; + } + const float final_alpha = Traits::range * brush_fade * brush_strength * + brush_alpha_pressure * grid_alpha; + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[l_index] = vpaint_blend<Color, Traits>(vp, + lcol[l_index], + color_orig, + *col, + final_alpha, + Traits::range * brush_strength); + } + } + } + } } } } - } - } - BKE_pbvh_vertex_iter_end; + BKE_pbvh_vertex_iter_end; + }; + }); } -static float tex_color_alpha_ubyte(SculptThreadedTaskData *data, - const float v_co[3], - uint *r_color) +template<class Color = ColorPaint4b, typename Traits = ByteTraits> +static void do_vpaint_brush_blur_verts(bContext *C, + Sculpt *UNUSED(sd), + VPaint *vp, + VPaintData<Color, Traits, ATTR_DOMAIN_POINT> *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode, + Color *lcol) { - float rgba[4]; - float rgba_br[3]; - tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba); - rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol); - mul_v3_v3(rgba_br, rgba); - rgb_float_to_uchar((uchar *)r_color, rgba_br); - return rgba[3]; -} + using Blend = typename Traits::BlendType; -static void do_vpaint_brush_draw_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + SculptSession *ss = ob->sculpt; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - const Scene *scene = CTX_data_scene(data->C); - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data( - scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & - (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const Brush *brush = ob->sculpt->cache->brush; + const Scene *scene = CTX_data_scene(C); + + Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { + for (int n : range) { + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const StrokeCache *cache = ss->cache; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(vp); + const bool use_vert_sel = (me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( + ss, brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_v3v3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( - ss, data->brush->falloff_shape); + /* Get the average poly color */ + Color color_final(0, 0, 0, 0); - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* NOTE: Grids are 1:1 with corners (aka loops). - * For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; + int total_hit_loops = 0; + Blend blend[4] = {0}; - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : - 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff( - &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { - const float brush_fade = BKE_brush_curve_strength( - brush, sqrtf(test.dist), cache->radius); - uint color_final = data->vpd->paintcol; - - /* If we're painting with a texture, sample the texture color and alpha. */ - float tex_alpha = 1.0; - if (data->vpd->is_texbrush) { - /* NOTE: we may want to paint alpha as vertex color alpha. */ - tex_alpha = tex_color_alpha_ubyte( - data, data->vpd->vertexcosnos[v_index].co, &color_final); - } - /* For each poly owning this vert, paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const uint l_index = mp->loopstart + k; + const uint v_index = me->mloop[l_index].v; + + Color *col = lcol + v_index; + + /* Color is squared to compensate the sqrt color encoding. */ + blend[0] += (Blend)col->r * (Blend)col->r; + blend[1] += (Blend)col->g * (Blend)col->g; + blend[2] += (Blend)col->b * (Blend)col->b; + blend[3] += (Blend)col->a * (Blend)col->a; + } + } + } + + if (total_hit_loops != 0) { + /* Use rgb^2 color averaging. */ + Color *col = &color_final; + + color_final.r = Traits::round( + sqrtf(Traits::divide_round(blend[0], total_hit_loops))); + color_final.g = Traits::round( + sqrtf(Traits::divide_round(blend[1], total_hit_loops))); + color_final.b = Traits::round( + sqrtf(Traits::divide_round(blend[2], total_hit_loops))); + color_final.a = Traits::round( + sqrtf(Traits::divide_round(blend[3], total_hit_loops))); + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + + BLI_assert(me->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index); + + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ + + if (previous_color != nullptr) { + /* Get the previous loop color */ + if (isZero(previous_color[v_index])) { + previous_color[v_index] = lcol[v_index]; + } + color_orig = previous_color[v_index]; + } + const float final_alpha = Traits::range * brush_fade * brush_strength * + brush_alpha_pressure * grid_alpha; + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[v_index] = vpaint_blend<Color, Traits>(vp, + lcol[v_index], + color_orig, + *col, + final_alpha, + Traits::range * brush_strength); + } } - color_orig = ss->mode.vpaint.previous_color[l_index]; } - const float final_alpha = 255 * brush_fade * brush_strength * tex_alpha * - brush_alpha_pressure * grid_alpha; - - /* Mix the new color with the original based on final_alpha. */ - lcol[l_index] = vpaint_blend(data->vp, - lcol[l_index], - color_orig, - color_final, - final_alpha, - 255 * brush_strength); } } } } - } - } - BKE_pbvh_vertex_iter_end; + BKE_pbvh_vertex_iter_end; + }; + }); } -static void do_vpaint_brush_blur_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) +template<typename Color = ColorPaint4b, typename Traits, AttributeDomain domain> +static void do_vpaint_brush_smear(bContext *C, + Sculpt *UNUSED(sd), + VPaint *vp, + VPaintData<Color, Traits, domain> *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode, + Color *lcol) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; + SculptSession *ss = ob->sculpt; + + const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const StrokeCache *cache = ss->cache; const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); const bool has_grids = (pbvh_type == PBVH_GRIDS); - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data( - scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & - (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const Brush *brush = ob->sculpt->cache->brush; + const Scene *scene = CTX_data_scene(C); + Color *color_curr = static_cast<Color *>(vpd->smear.color_curr); + Color *color_prev_smear = static_cast<Color *>(vpd->smear.color_prev); + Color *color_prev = reinterpret_cast<Color *>(ss->cache->prev_colors_vpaint); + + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { + for (int n : range) { + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + float brush_dir[3]; + const bool use_normal = vwpaint_use_normal(vp); + const bool use_vert_sel = (me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + sub_v3_v3v3(brush_dir, cache->location, cache->last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( + ss, brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &me->mvert[v_index]; + + /* if the vertex is selected for painting. */ + if (!use_vert_sel || mv_curr->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_v3v3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + + bool do_color = false; + /* Minimum dot product between brush direction and current + * to neighbor direction is 0.0, meaning orthogonal. */ + float stroke_dot_max = 0.0f; + + /* Get the color of the loop in the opposite + * direction of the brush movement */ + Color color_final(0, 0, 0, 0); + + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(me->mloop[l_index].v == v_index); + UNUSED_VARS_NDEBUG(l_index); + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + const MLoop *ml_other = &me->mloop[mp->loopstart]; + for (int k = 0; k < mp->totloop; k++, ml_other++) { + const uint v_other_index = ml_other->v; + if (v_other_index != v_index) { + const MVert *mv_other = &me->mvert[v_other_index]; + + /* Get the direction from the + * selected vert to the neighbor. */ + float other_dir[3]; + sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); + project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + + normalize_v3(other_dir); + + const float stroke_dot = dot_v3v3(other_dir, brush_dir); + int elem_index; + + if constexpr (domain == ATTR_DOMAIN_POINT) { + elem_index = ml_other->v; + } + else { + elem_index = mp->loopstart + k; + } + + if (stroke_dot > stroke_dot_max) { + stroke_dot_max = stroke_dot; + color_final = color_prev_smear[elem_index]; + do_color = true; + } + } + } + } + } - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( - ss, data->brush->falloff_shape); + if (do_color) { + const float final_alpha = Traits::range * brush_fade * brush_strength * + brush_alpha_pressure * grid_alpha; - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? dot_v3v3(sculpt_normal_frontface, vd.no) : - 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff( - &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { - const float brush_fade = BKE_brush_curve_strength( - brush, sqrtf(test.dist), cache->radius); + int elem_index; + if constexpr (domain == ATTR_DOMAIN_POINT) { + elem_index = v_index; + } + else { + elem_index = l_index; + } - /* Get the average poly color */ - uint color_final = 0; - int total_hit_loops = 0; - uint blend[4] = {0}; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - total_hit_loops += mp->totloop; - for (int k = 0; k < mp->totloop; k++) { - const uint l_index = mp->loopstart + k; - const char *col = (const char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - blend[0] += (uint)col[0] * (uint)col[0]; - blend[1] += (uint)col[1] * (uint)col[1]; - blend[2] += (uint)col[2] * (uint)col[2]; - blend[3] += (uint)col[3] * (uint)col[3]; + BLI_assert(me->mloop[l_index].v == v_index); + + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + /* Get the previous element color */ + Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ + + if (color_prev != nullptr) { + /* Get the previous element color */ + if (isZero(color_prev[elem_index])) { + color_prev[elem_index] = lcol[elem_index]; + } + color_orig = color_prev[elem_index]; + } + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[elem_index] = vpaint_blend<Color, Traits>(vp, + lcol[elem_index], + color_orig, + color_final, + final_alpha, + Traits::range * + brush_strength); + + color_curr[elem_index] = lcol[elem_index]; + } + } + } } } } - if (total_hit_loops != 0) { - /* Use rgb^2 color averaging. */ - char *col = (char *)(&color_final); - col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops))); - col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops))); - col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops))); - col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops))); - - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; + } + BKE_pbvh_vertex_iter_end; + } + } + }); +} + +template<typename Color, typename Traits, AttributeDomain domain> +static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd, + Object *ob, + Mesh *me, + const Brush *brush, + Color *lcol, + PBVHNode **nodes, + int totnode) +{ + using Blend = typename Traits::BlendType; + + VPaintAverageAccum<Blend> *accum = (VPaintAverageAccum<Blend> *)MEM_mallocN( + sizeof(*accum) * totnode, __func__); + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { + for (int n : range) { + SculptSession *ss = ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + StrokeCache *cache = ss->cache; + const bool use_vert_sel = (me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + + VPaintAverageAccum<Blend> *accum2 = accum + n; + accum2->len = 0; + memset(accum2->value, 0, sizeof(accum2->value)); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + if (BKE_brush_curve_strength(brush, 0.0, cache->radius) > 0.0) { + /* If the vertex is selected for painting. */ + const MVert *mv = &me->mvert[v_index]; + if (!use_vert_sel || mv->flag & SELECT) { + accum2->len += gmap->vert_to_loop[v_index].count; + /* if a vertex is within the brush region, then add its color to the blend. */ + for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { + int elem_index; + + if constexpr (domain == ATTR_DOMAIN_CORNER) { + elem_index = gmap->vert_to_loop[v_index].indices[j]; + } + else { + elem_index = v_index; } - const float final_alpha = 255 * brush_fade * brush_strength * - brush_alpha_pressure * grid_alpha; - /* Mix the new color with the original - * based on the brush strength and the curve. */ - lcol[l_index] = vpaint_blend(data->vp, - lcol[l_index], - color_orig, - *((uint *)col), - final_alpha, - 255 * brush_strength); + + Color *col = lcol + elem_index; + + /* Color is squared to compensate the sqrt color encoding. */ + accum2->value[0] += col->r * col->r; + accum2->value[1] += col->g * col->g; + accum2->value[2] += col->b * col->b; } } } } } + BKE_pbvh_vertex_iter_end; } + }); + + Blend accum_len = 0; + Blend accum_value[3] = {0}; + Color blend(0, 0, 0, 0); + + for (int i = 0; i < totnode; i++) { + accum_len += accum[i].len; + accum_value[0] += accum[i].value[0]; + accum_value[1] += accum[i].value[1]; + accum_value[2] += accum[i].value[2]; + } + if (accum_len != 0) { + blend.r = Traits::round(sqrtf(Traits::divide_round(accum_value[0], accum_len))); + blend.g = Traits::round(sqrtf(Traits::divide_round(accum_value[1], accum_len))); + blend.b = Traits::round(sqrtf(Traits::divide_round(accum_value[2], accum_len))); + blend.a = Traits::range; + + vpd->paintcol = blend; } - BKE_pbvh_vertex_iter_end; } -static void do_vpaint_brush_smear_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) +template<typename Color, typename Traits, AttributeDomain domain> +static float paint_and_tex_color_alpha(VPaint *vp, + VPaintData<Color, Traits, domain> *vpd, + const float v_co[3], + Color *r_color) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data( - scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - float brush_dir[3]; - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & - (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + ColorPaint4f rgba; + ColorPaint4f rgba_br = toFloat(*r_color); - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + paint_and_tex_color_alpha_intern(vp, &vpd->vc, v_co, &rgba.r); - if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + rgb_uchar_to_float(&rgba_br.r, (const uchar *)&vpd->paintcol); + mul_v3_v3(rgba_br, rgba); - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( - ss, data->brush->falloff_shape); + *r_color = fromFloat<Color>(rgba_br); + return rgba[3]; +} - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : - vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; +template<typename Color, typename Traits, AttributeDomain domain> +static void vpaint_do_draw(bContext *C, + Sculpt *UNUSED(sd), + VPaint *vp, + VPaintData<Color, Traits, domain> *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode, + Color *lcol) +{ + SculptSession *ss = ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - /* if the vertex is selected for painting. */ - if (!use_vert_sel || mv_curr->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_v3v3(sculpt_normal_frontface, vd.no) : - 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff( - &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { - const float brush_fade = BKE_brush_curve_strength( - brush, sqrtf(test.dist), cache->radius); + const Brush *brush = ob->sculpt->cache->brush; + const Scene *scene = CTX_data_scene(C); + + Color *previous_color = static_cast<Color *>(ss->cache->prev_colors_vpaint); + + blender::threading::parallel_for(IndexRange(totnode), 1LL, [&](IndexRange range) { + for (int n : range) { + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + const StrokeCache *cache = ss->cache; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(vp); + const bool use_vert_sel = (me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( + ss, brush->falloff_shape); + + Color paintcol = vpd->paintcol; + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* NOTE: Grids are 1:1 with corners (aka loops). + * For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_v3v3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); - bool do_color = false; - /* Minimum dot product between brush direction and current - * to neighbor direction is 0.0, meaning orthogonal. */ - float stroke_dot_max = 0.0f; + Color color_final = paintcol; - /* Get the color of the loop in the opposite - * direction of the brush movement */ - uint color_final = 0; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - UNUSED_VARS_NDEBUG(l_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; - for (int k = 0; k < mp->totloop; k++, ml_other++) { - const uint v_other_index = ml_other->v; - if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; - - /* Get the direction from the - * selected vert to the neighbor. */ - float other_dir[3]; - sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); - - normalize_v3(other_dir); - - const float stroke_dot = dot_v3v3(other_dir, brush_dir); - - if (stroke_dot > stroke_dot_max) { - stroke_dot_max = stroke_dot; - color_final = data->vpd->smear.color_prev[mp->loopstart + k]; - do_color = true; - } - } - } + /* If we're painting with a texture, sample the texture color and alpha. */ + float tex_alpha = 1.0; + if (vpd->is_texbrush) { + /* NOTE: we may want to paint alpha as vertex color alpha. */ + tex_alpha = paint_and_tex_color_alpha<Color, Traits, domain>( + vp, vpd, vpd->vertexcosnos[v_index].co, &color_final); } - } - if (do_color) { - const float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure * - grid_alpha; + Color color_orig(0, 0, 0, 0); - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { + if constexpr (domain == ATTR_DOMAIN_POINT) { + int v_index = vd.index; + + if (previous_color != nullptr) { /* Get the previous loop color */ - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + if (isZero(previous_color[v_index])) { + previous_color[v_index] = lcol[v_index]; + } + color_orig = previous_color[v_index]; + } + const float final_alpha = Traits::frange * brush_fade * brush_strength * + tex_alpha * brush_alpha_pressure * grid_alpha; + + lcol[v_index] = vpaint_blend<Color, Traits>(vp, + lcol[v_index], + color_orig, + color_final, + final_alpha, + Traits::range * brush_strength); + } + else { + /* For each poly owning this vert, paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(me->mloop[l_index].v == v_index); + const MPoly *mp = &me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */ + + if (previous_color != nullptr) { + /* Get the previous loop color */ + if (isZero(previous_color[l_index])) { + previous_color[l_index] = lcol[l_index]; + } + color_orig = previous_color[l_index]; } - color_orig = ss->mode.vpaint.previous_color[l_index]; + const float final_alpha = Traits::frange * brush_fade * brush_strength * + tex_alpha * brush_alpha_pressure * grid_alpha; + + /* Mix the new color with the original based on final_alpha. */ + lcol[l_index] = vpaint_blend<Color, Traits>(vp, + lcol[l_index], + color_orig, + color_final, + final_alpha, + Traits::range * brush_strength); } - /* Mix the new color with the original - * based on the brush strength and the curve. */ - lcol[l_index] = vpaint_blend(data->vp, - lcol[l_index], - color_orig, - color_final, - final_alpha, - 255 * brush_strength); - - data->vpd->smear.color_curr[l_index] = lcol[l_index]; } } } } } } + BKE_pbvh_vertex_iter_end; } - BKE_pbvh_vertex_iter_end; - } + }); } -static void calculate_average_color(SculptThreadedTaskData *data, - PBVHNode **UNUSED(nodes), - int totnode) +template<typename Color, typename Traits, AttributeDomain domain> +static void vpaint_do_blur(bContext *C, + Sculpt *sd, + VPaint *vp, + VPaintData<Color, Traits, domain> *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode, + Color *lcol) { - struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); - data->custom_data = accum; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); - - uint accum_len = 0; - uint accum_value[3] = {0}; - uchar blend[4] = {0}; - for (int i = 0; i < totnode; i++) { - accum_len += accum[i].len; - accum_value[0] += accum[i].value[0]; - accum_value[1] += accum[i].value[1]; - accum_value[2] += accum[i].value[2]; + if constexpr (domain == ATTR_DOMAIN_POINT) { + do_vpaint_brush_blur_verts<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol); } - if (accum_len != 0) { - blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len))); - blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len))); - blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len))); - blend[3] = 255; - data->vpd->paintcol = *((uint *)blend); + else { + do_vpaint_brush_blur_loops<Color, Traits>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol); } - - MEM_SAFE_FREE(data->custom_data); /* 'accum' */ } +template<typename Color, typename Traits, AttributeDomain domain> static void vpaint_paint_leaves(bContext *C, Sculpt *sd, VPaint *vp, - struct VPaintData *vpd, + VPaintData<Color, Traits, domain> *vpd, Object *ob, Mesh *me, + Color *lcol, PBVHNode **nodes, int totnode) { + + for (int i : IndexRange(totnode)) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR); + } + const Brush *brush = ob->sculpt->cache->brush; - SculptThreadedTaskData data = { - .C = C, - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .vp = vp, - .vpd = vpd, - .lcol = (uint *)me->mloopcol, - .me = me, - }; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { case VPAINT_TOOL_AVERAGE: - calculate_average_color(&data, nodes, totnode); - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + calculate_average_color<Color, Traits, domain>(vpd, ob, me, brush, lcol, nodes, totnode); + break; + case VPAINT_TOOL_DRAW: + vpaint_do_draw<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol); break; case VPAINT_TOOL_BLUR: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); + vpaint_do_blur<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol); break; case VPAINT_TOOL_SMEAR: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); + do_vpaint_brush_smear<Color, Traits, domain>(C, sd, vp, vpd, ob, me, nodes, totnode, lcol); break; - case VPAINT_TOOL_DRAW: - BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + default: break; } } +template<typename Color, typename Traits, AttributeDomain domain> static void vpaint_do_paint(bContext *C, Sculpt *sd, VPaint *vp, - struct VPaintData *vpd, + VPaintData<Color, Traits, domain> *vpd, Object *ob, Mesh *me, Brush *brush, @@ -3339,18 +3767,22 @@ static void vpaint_do_paint(bContext *C, int totnode; PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + Color *color_data = static_cast<Color *>(layer->data); + /* Paint those leaves. */ - vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode); + vpaint_paint_leaves<Color, Traits, domain>(C, sd, vp, vpd, ob, me, color_data, nodes, totnode); if (nodes) { MEM_freeN(nodes); } } +template<typename Color, typename Traits, AttributeDomain domain> static void vpaint_do_radial_symmetry(bContext *C, Sculpt *sd, VPaint *vp, - struct VPaintData *vpd, + VPaintData<Color, Traits, domain> *vpd, Object *ob, Mesh *me, Brush *brush, @@ -3359,17 +3791,18 @@ static void vpaint_do_radial_symmetry(bContext *C, { for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); + vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); } } /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ +template<typename Color, typename Traits, AttributeDomain domain> static void vpaint_do_symmetrical_brush_actions( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob) + bContext *C, Sculpt *sd, VPaint *vp, VPaintData<Color, Traits, domain> *vpd, Object *ob) { Brush *brush = BKE_paint_brush(&vp->paint); - Mesh *me = ob->data; + Mesh *me = (Mesh *)ob->data; SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -3377,10 +3810,10 @@ static void vpaint_do_symmetrical_brush_actions( /* initial stroke */ cache->mirror_symmetry_pass = 0; - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X'); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z'); cache->symmetry = symm; @@ -3393,16 +3826,16 @@ static void vpaint_do_symmetrical_brush_actions( SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); if (i & (1 << 0)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'X'); } if (i & (1 << 1)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Y'); } if (i & (1 << 2)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + vpaint_do_paint<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); + vpaint_do_radial_symmetry<Color, Traits, domain>(C, sd, vp, vpd, ob, me, brush, i, 'Z'); } } } @@ -3411,14 +3844,13 @@ static void vpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -static void vpaint_stroke_update_step(bContext *C, - wmOperator *UNUSED(op), - struct PaintStroke *stroke, - PointerRNA *itemptr) +template<typename Color, typename Traits, AttributeDomain domain> +static void vpaint_stroke_update_step_intern(bContext *C, PaintStroke *stroke, PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); - struct VPaintData *vpd = paint_stroke_mode_data(stroke); + VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>( + paint_stroke_mode_data(stroke)); VPaint *vp = ts->vpaint; ViewContext *vc = &vpd->vc; Object *ob = vc->obact; @@ -3436,15 +3868,20 @@ static void vpaint_stroke_update_step(bContext *C, swap_m4m4(vc->rv3d->persmat, mat); - vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob); + vpaint_do_symmetrical_brush_actions<Color, Traits, domain>(C, sd, vp, vpd, ob); swap_m4m4(vc->rv3d->persmat, mat); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { - memcpy( - vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop); + Mesh *me = BKE_object_get_original_mesh(ob); + + size_t elem_size; + int elem_num; + + elem_num = get_vcol_elements(me, &elem_size); + memcpy(vpd->smear.color_prev, vpd->smear.color_curr, elem_size * elem_num); } /* Calculate pivot for rotation around selection if needed. @@ -3458,19 +3895,47 @@ static void vpaint_stroke_update_step(bContext *C, if (vpd->use_fast_update == false) { /* recalculate modifier stack to get new colors, slow, * avoid this if we can! */ - DEG_id_tag_update(ob->data, 0); + DEG_id_tag_update((ID *)ob->data, 0); } else { /* Flush changes through DEG. */ - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE); } } -static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) +static void vpaint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + PaintStroke *stroke, + PointerRNA *itemptr) { - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - ViewContext *vc = &vpd->vc; - Object *ob = vc->obact; + VPaintDataBase *vpd = static_cast<VPaintDataBase *>(paint_stroke_mode_data(stroke)); + + if (vpd->domain == ATTR_DOMAIN_POINT) { + if (vpd->type == CD_PROP_COLOR) { + vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>( + C, stroke, itemptr); + } + else if (vpd->type == CD_PROP_BYTE_COLOR) { + vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>( + C, stroke, itemptr); + } + } + else if (vpd->domain == ATTR_DOMAIN_CORNER) { + if (vpd->type == CD_PROP_COLOR) { + vpaint_stroke_update_step_intern<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>( + C, stroke, itemptr); + } + else if (vpd->type == CD_PROP_BYTE_COLOR) { + vpaint_stroke_update_step_intern<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>( + C, stroke, itemptr); + } + } +} + +template<typename Color, typename Traits, AttributeDomain domain> +static void vpaint_free_vpaintdata(Object *UNUSED(ob), void *_vpd) +{ + VPaintData<Color, Traits, domain> *vpd = static_cast<VPaintData<Color, Traits, domain> *>(_vpd); if (vpd->is_texbrush) { ED_vpaint_proj_handle_free(vpd->vp_handle); @@ -3486,6 +3951,34 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) MEM_freeN(vpd->smear.color_curr); } + MEM_delete<VPaintData<Color, Traits, domain>>(vpd); +} + +static void vpaint_stroke_done(const bContext *C, PaintStroke *stroke) +{ + void *vpd_ptr = paint_stroke_mode_data(stroke); + VPaintDataBase *vpd = static_cast<VPaintDataBase *>(vpd_ptr); + + ViewContext *vc = &vpd->vc; + Object *ob = vc->obact; + + if (vpd->domain == ATTR_DOMAIN_POINT) { + if (vpd->type == CD_PROP_COLOR) { + vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>(ob, vpd); + } + else if (vpd->type == CD_PROP_BYTE_COLOR) { + vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>(ob, vpd); + } + } + else if (vpd->domain == ATTR_DOMAIN_CORNER) { + if (vpd->type == CD_PROP_COLOR) { + vpaint_free_vpaintdata<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>(ob, vpd); + } + else if (vpd->type == CD_PROP_BYTE_COLOR) { + vpaint_free_vpaintdata<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>(ob, vpd); + } + } + SculptSession *ss = ob->sculpt; if (ss->cache->alt_smooth) { @@ -3496,10 +3989,10 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - MEM_freeN(vpd); + SCULPT_undo_push_end(ob); SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -3511,12 +4004,20 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) SCULPT_stroke_get_location, vpaint_stroke_test_start, vpaint_stroke_update_step, - NULL, + nullptr, vpaint_stroke_done, event->type); + Object *ob = CTX_data_active_object(C); + + if (SCULPT_has_loop_colors(ob) && ob->sculpt->pbvh) { + BKE_pbvh_ensure_node_loops(ob->sculpt->pbvh); + } + + SCULPT_undo_push_begin(ob, "Vertex Paint"); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op, op->customdata); + paint_stroke_free(C, op, (PaintStroke *)op->customdata); return OPERATOR_FINISHED; } @@ -3536,12 +4037,12 @@ static int vpaint_exec(bContext *C, wmOperator *op) SCULPT_stroke_get_location, vpaint_stroke_test_start, vpaint_stroke_update_step, - NULL, + nullptr, vpaint_stroke_done, 0); /* frees op->customdata */ - paint_stroke_exec(C, op, op->customdata); + paint_stroke_exec(C, op, (PaintStroke *)op->customdata); return OPERATOR_FINISHED; } @@ -3551,15 +4052,15 @@ static void vpaint_cancel(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if (ob->sculpt->cache) { SCULPT_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + ob->sculpt->cache = nullptr; } - paint_stroke_cancel(C, op, op->customdata); + paint_stroke_cancel(C, op, (PaintStroke *)op->customdata); } static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) { - return paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata); + return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata); } void PAINT_OT_vertex_paint(wmOperatorType *ot) @@ -3582,4 +4083,113 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) paint_stroke_operator_properties(ot); } +/* -------------------------------------------------------------------- */ +/** \name Set Vertex Colors Operator + * \{ */ + +template<typename Color, typename Traits, AttributeDomain domain> +static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, Color *color_layer) +{ + Mesh *me; + if (((me = BKE_mesh_from_object(ob)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == false)) { + return false; + } + + Color paintcol = fromFloat<Color>(paintcol_in); + + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + const MPoly *mp = me->mpoly; + for (int i = 0; i < me->totpoly; i++, mp++) { + if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { + continue; + } + + int j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + + if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { + if constexpr (domain == ATTR_DOMAIN_CORNER) { + color_layer[mp->loopstart + j] = paintcol; + } + else { + color_layer[vidx] = paintcol; + } + } + j++; + } while (j < mp->totloop); + } + + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); + + DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); + + /* NOTE: Original mesh is used for display, so tag it directly here. */ + BKE_mesh_batch_cache_dirty_tag(me, BKE_MESH_BATCH_DIRTY_ALL); + + return true; +} + +static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Object *obact = CTX_data_active_object(C); + Mesh *me = BKE_object_get_original_mesh(obact); + + // uint paintcol = vpaint_get_current_color(scene, scene->toolsettings->vpaint, false); + ColorPaint4f paintcol = vpaint_get_current_col<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>( + scene, scene->toolsettings->vpaint, false); + + bool ok = false; + + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + AttributeDomain domain = BKE_id_attribute_domain(&me->id, layer); + + if (domain == ATTR_DOMAIN_POINT) { + if (layer->type == CD_PROP_COLOR) { + ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_POINT>( + obact, paintcol, static_cast<ColorPaint4f *>(layer->data)); + } + else if (layer->type == CD_PROP_BYTE_COLOR) { + ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_POINT>( + obact, paintcol, static_cast<ColorPaint4b *>(layer->data)); + } + } + else { + if (layer->type == CD_PROP_COLOR) { + ok = vertex_color_set<ColorPaint4f, FloatTraits, ATTR_DOMAIN_CORNER>( + obact, paintcol, static_cast<ColorPaint4f *>(layer->data)); + } + else if (layer->type == CD_PROP_BYTE_COLOR) { + ok = vertex_color_set<ColorPaint4b, ByteTraits, ATTR_DOMAIN_CORNER>( + obact, paintcol, static_cast<ColorPaint4b *>(layer->data)); + } + } + + if (ok) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Vertex Colors"; + ot->idname = "PAINT_OT_vertex_color_set"; + ot->description = "Fill the active vertex color layer with the current paint color"; + + /* api callbacks */ + ot->exec = vertex_color_set_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 619607408e5..9f337d8d789 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -47,75 +47,6 @@ static void tag_object_after_update(Object *object) BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); } -/* -------------------------------------------------------------------- */ -/** \name Set Vertex Colors Operator - * \{ */ - -static bool vertex_color_set(Object *ob, uint paintcol) -{ - Mesh *me; - if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { - return false; - } - - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - const MPoly *mp = me->mpoly; - for (int i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = me->mloopcol + mp->loopstart; - - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { - continue; - } - - int j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; - if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { - *(int *)lcol = paintcol; - } - lcol++; - j++; - } while (j < mp->totloop); - } - - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); - - tag_object_after_update(ob); - - return true; -} - -static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - Object *obact = CTX_data_active_object(C); - uint paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false); - - if (vertex_color_set(obact, paintcol)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void PAINT_OT_vertex_color_set(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Set Vertex Colors"; - ot->idname = "PAINT_OT_vertex_color_set"; - ot->description = "Fill the active vertex color layer with the current paint color"; - - /* api callbacks */ - ot->exec = vertex_color_set_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c index 8d5f39fd56e..cd099f71ccd 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c @@ -73,570 +73,3 @@ bool ED_vpaint_color_transform(struct Object *ob, return true; } - -/* -------------------------------------------------------------------- */ -/** \name Color Blending Modes - * \{ */ - -BLI_INLINE uint mcol_blend(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - if (fac >= 255) { - return col_dst; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* Updated to use the rgb squared color model which blends nicer. */ - int r1 = cp_src[0] * cp_src[0]; - int g1 = cp_src[1] * cp_src[1]; - int b1 = cp_src[2] * cp_src[2]; - int a1 = cp_src[3] * cp_src[3]; - - int r2 = cp_dst[0] * cp_dst[0]; - int g2 = cp_dst[1] * cp_dst[1]; - int b2 = cp_dst[2] * cp_dst[2]; - int a2 = cp_dst[3] * cp_dst[3]; - - cp_mix[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255))); - cp_mix[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255))); - cp_mix[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255))); - cp_mix[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255))); - - return col_mix; -} - -BLI_INLINE uint mcol_add(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[0] + divide_round_i((fac * cp_dst[0]), 255); - cp_mix[0] = (temp > 254) ? 255 : temp; - temp = cp_src[1] + divide_round_i((fac * cp_dst[1]), 255); - cp_mix[1] = (temp > 254) ? 255 : temp; - temp = cp_src[2] + divide_round_i((fac * cp_dst[2]), 255); - cp_mix[2] = (temp > 254) ? 255 : temp; - temp = cp_src[3] + divide_round_i((fac * cp_dst[3]), 255); - cp_mix[3] = (temp > 254) ? 255 : temp; - - return col_mix; -} - -BLI_INLINE uint mcol_sub(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[0] - divide_round_i((fac * cp_dst[0]), 255); - cp_mix[0] = (temp < 0) ? 0 : temp; - temp = cp_src[1] - divide_round_i((fac * cp_dst[1]), 255); - cp_mix[1] = (temp < 0) ? 0 : temp; - temp = cp_src[2] - divide_round_i((fac * cp_dst[2]), 255); - cp_mix[2] = (temp < 0) ? 0 : temp; - temp = cp_src[3] - divide_round_i((fac * cp_dst[3]), 255); - cp_mix[3] = (temp < 0) ? 0 : temp; - - return col_mix; -} - -BLI_INLINE uint mcol_mul(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* first mul, then blend the fac */ - cp_mix[0] = divide_round_i(mfac * cp_src[0] * 255 + fac * cp_dst[0] * cp_src[0], 255 * 255); - cp_mix[1] = divide_round_i(mfac * cp_src[1] * 255 + fac * cp_dst[1] * cp_src[1], 255 * 255); - cp_mix[2] = divide_round_i(mfac * cp_src[2] * 255 + fac * cp_dst[2] * cp_src[2], 255 * 255); - cp_mix[3] = divide_round_i(mfac * cp_src[3] * 255 + fac * cp_dst[3] * cp_src[3], 255 * 255); - - return col_mix; -} - -BLI_INLINE uint mcol_lighten(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - if (fac >= 255) { - return col_dst; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* See if we're lighter, if so mix, else don't do anything. - * if the paint color is darker then the original, then ignore */ - if (IMB_colormanagement_get_luminance_byte(cp_src) > - IMB_colormanagement_get_luminance_byte(cp_dst)) { - return col_src; - } - - cp_mix[0] = divide_round_i(mfac * cp_src[0] + fac * cp_dst[0], 255); - cp_mix[1] = divide_round_i(mfac * cp_src[1] + fac * cp_dst[1], 255); - cp_mix[2] = divide_round_i(mfac * cp_src[2] + fac * cp_dst[2], 255); - cp_mix[3] = divide_round_i(mfac * cp_src[3] + fac * cp_dst[3], 255); - - return col_mix; -} - -BLI_INLINE uint mcol_darken(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - if (fac >= 255) { - return col_dst; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* See if we're darker, if so mix, else don't do anything. - * if the paint color is brighter then the original, then ignore */ - if (IMB_colormanagement_get_luminance_byte(cp_src) < - IMB_colormanagement_get_luminance_byte(cp_dst)) { - return col_src; - } - - cp_mix[0] = divide_round_i((mfac * cp_src[0] + fac * cp_dst[0]), 255); - cp_mix[1] = divide_round_i((mfac * cp_src[1] + fac * cp_dst[1]), 255); - cp_mix[2] = divide_round_i((mfac * cp_src[2] + fac * cp_dst[2]), 255); - cp_mix[3] = divide_round_i((mfac * cp_src[3] + fac * cp_dst[3]), 255); - return col_mix; -} - -BLI_INLINE uint mcol_colordodge(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = (cp_dst[0] == 255) ? 255 : min_ii((cp_src[0] * 225) / (255 - cp_dst[0]), 255); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = (cp_dst[1] == 255) ? 255 : min_ii((cp_src[1] * 225) / (255 - cp_dst[1]), 255); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = (cp_dst[2] == 255) ? 255 : min_ii((cp_src[2] * 225) / (255 - cp_dst[2]), 255); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = (cp_dst[3] == 255) ? 255 : min_ii((cp_src[3] * 225) / (255 - cp_dst[3]), 255); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_difference(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = abs(cp_src[0] - cp_dst[0]); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = abs(cp_src[1] - cp_dst[1]); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = abs(cp_src[2] - cp_dst[2]); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = abs(cp_src[3] - cp_dst[3]); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_screen(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = max_ii(255 - (((255 - cp_src[0]) * (255 - cp_dst[0])) / 255), 0); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[1]) * (255 - cp_dst[1])) / 255), 0); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[2]) * (255 - cp_dst[2])) / 255), 0); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[3]) * (255 - cp_dst[3])) / 255), 0); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_hardlight(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - if (cp_dst[i] > 127) { - temp = 255 - ((255 - 2 * (cp_dst[i] - 127)) * (255 - cp_src[i]) / 255); - } - else { - temp = (2 * cp_dst[i] * cp_src[i]) >> 8; - } - cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); - } - return col_mix; -} - -BLI_INLINE uint mcol_overlay(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - if (cp_src[i] > 127) { - temp = 255 - ((255 - 2 * (cp_src[i] - 127)) * (255 - cp_dst[i]) / 255); - } - else { - temp = (2 * cp_dst[i] * cp_src[i]) >> 8; - } - cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); - } - return col_mix; -} - -BLI_INLINE uint mcol_softlight(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - for (int i = 0; i < 4; i++) { - if (cp_src[i] < 127) { - temp = ((2 * ((cp_dst[i] / 2) + 64)) * cp_src[i]) / 255; - } - else { - temp = 255 - (2 * (255 - ((cp_dst[i] / 2) + 64)) * (255 - cp_src[i]) / 255); - } - cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; - } - return col_mix; -} - -BLI_INLINE uint mcol_exclusion(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - temp = 127 - ((2 * (cp_src[i] - 127) * (cp_dst[i] - 127)) / 255); - cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; - } - return col_mix; -} - -BLI_INLINE uint mcol_luminosity(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - - v1 = v2; - - hsv_to_rgb(h1, s1, v1, &r, &g, &b); - - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_saturation(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - - if (s1 > EPS_SATURATION) { - s1 = s2; - } - - hsv_to_rgb(h1, s1, v1, &r, &g, &b); - - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_hue(uint col_src, uint col_dst, int fac) -{ - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - - h1 = h2; - - hsv_to_rgb(h1, s1, v1, &r, &g, &b); - - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; - return col_mix; -} - -BLI_INLINE uint mcol_alpha_add(uint col_src, int fac) -{ - uchar *cp_src, *cp_mix; - int temp; - uint col_mix = col_src; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[3] + fac; - cp_mix[3] = (temp > 254) ? 255 : temp; - - return col_mix; -} - -BLI_INLINE uint mcol_alpha_sub(uint col_src, int fac) -{ - uchar *cp_src, *cp_mix; - int temp; - uint col_mix = col_src; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[3] - fac; - cp_mix[3] = temp < 0 ? 0 : temp; - - return col_mix; -} - -uint ED_vpaint_blend_tool(const int tool, const uint col, const uint paintcol, const int alpha_i) -{ - switch ((IMB_BlendMode)tool) { - case IMB_BLEND_MIX: - return mcol_blend(col, paintcol, alpha_i); - case IMB_BLEND_ADD: - return mcol_add(col, paintcol, alpha_i); - case IMB_BLEND_SUB: - return mcol_sub(col, paintcol, alpha_i); - case IMB_BLEND_MUL: - return mcol_mul(col, paintcol, alpha_i); - case IMB_BLEND_LIGHTEN: - return mcol_lighten(col, paintcol, alpha_i); - case IMB_BLEND_DARKEN: - return mcol_darken(col, paintcol, alpha_i); - case IMB_BLEND_COLORDODGE: - return mcol_colordodge(col, paintcol, alpha_i); - case IMB_BLEND_DIFFERENCE: - return mcol_difference(col, paintcol, alpha_i); - case IMB_BLEND_SCREEN: - return mcol_screen(col, paintcol, alpha_i); - case IMB_BLEND_HARDLIGHT: - return mcol_hardlight(col, paintcol, alpha_i); - case IMB_BLEND_OVERLAY: - return mcol_overlay(col, paintcol, alpha_i); - case IMB_BLEND_SOFTLIGHT: - return mcol_softlight(col, paintcol, alpha_i); - case IMB_BLEND_EXCLUSION: - return mcol_exclusion(col, paintcol, alpha_i); - case IMB_BLEND_LUMINOSITY: - return mcol_luminosity(col, paintcol, alpha_i); - case IMB_BLEND_SATURATION: - return mcol_saturation(col, paintcol, alpha_i); - case IMB_BLEND_HUE: - return mcol_hue(col, paintcol, alpha_i); - /* non-color */ - case IMB_BLEND_ERASE_ALPHA: - return mcol_alpha_sub(col, alpha_i); - case IMB_BLEND_ADD_ALPHA: - return mcol_alpha_add(col, alpha_i); - default: - BLI_assert(0); - return 0; - } -} - -/** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index bbbed32e316..93a60b9908e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2780,7 +2780,7 @@ static void sculpt_pbvh_update_pixels(PaintModeSettings *paint_mode_settings, return; } - BKE_pbvh_build_pixels(ss->pbvh, mesh->mloop, &mesh->ldata, image, image_user); + BKE_pbvh_build_pixels(ss->pbvh, mesh, image, image_user); } /** \} */ @@ -3097,8 +3097,7 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) for (a = 0; a < me->totvert; a++, mvert++) { copy_v3_v3(mvert->co, vertCos[a]); } - - BKE_mesh_calc_normals(me); + BKE_mesh_normals_tag_dirty(me); } /* Apply new coords on active key block, no need to re-allocate kb->data here! */ @@ -3915,16 +3914,6 @@ bool SCULPT_mode_poll(bContext *C) return ob && ob->mode & OB_MODE_SCULPT; } -bool SCULPT_vertex_colors_poll(bContext *C) -{ - if (!SCULPT_mode_poll(C)) { - return false; - } - - Object *ob = CTX_data_active_object(C); - return ob->sculpt && SCULPT_has_colors(ob->sculpt); -} - bool SCULPT_mode_poll_view3d(bContext *C) { return (SCULPT_mode_poll(C) && CTX_wm_region_view3d(C)); @@ -4026,6 +4015,7 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->detail_directions); MEM_SAFE_FREE(cache->prev_displacement); MEM_SAFE_FREE(cache->limit_surface_co); + MEM_SAFE_FREE(cache->prev_colors_vpaint); if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); @@ -5256,6 +5246,24 @@ static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float return SCULPT_stroke_get_location(C, co, mouse); } +bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return true; + case PBVH_BMESH: + BKE_report(reports, RPT_ERROR, "Not supported in dynamic topology mode"); + return false; + case PBVH_GRIDS: + BKE_report(reports, RPT_ERROR, "Not supported in multiresolution mode"); + return false; + } + + BLI_assert_msg(0, "PBVH corruption, type was invalid."); + + return false; +} + static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) { /* Don't start the stroke until mouse goes over the mesh. @@ -5438,6 +5446,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent sculpt_brush_stroke_init(C, op); + Object *ob = CTX_data_active_object(C); + + if (!SCULPT_handles_colors_report(ob->sculpt, op->reports)) { + return OPERATOR_CANCELLED; + } + stroke = paint_stroke_new(C, op, SCULPT_stroke_get_location, diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 5d4a2c54832..a705f6aa8a8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -24,6 +24,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_report.h" #include "BKE_scene.h" #include "IMB_colormanagement.h" @@ -342,10 +343,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent } /* Disable for multires and dyntopo for now */ - if (!ss->pbvh) { - return OPERATOR_CANCELLED; - } - if (BKE_pbvh_type(pbvh) != PBVH_FACES) { + if (!ss->pbvh || !SCULPT_handles_colors_report(ss, op->reports)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 3839c0e71e4..f13f1c79a7a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -219,7 +219,6 @@ typedef struct SculptThreadedTaskData { int totnode; struct VPaint *vp; - struct VPaintData *vpd; struct WPaintData *wpd; struct WeightPaintInfo *wpi; unsigned int *lcol; @@ -493,6 +492,7 @@ typedef struct StrokeCache { float mouse_event[2]; float (*prev_colors)[4]; + void *prev_colors_vpaint; /* Multires Displacement Smear. */ float (*prev_displacement)[3]; @@ -782,7 +782,17 @@ bool SCULPT_mode_poll_view3d(struct bContext *C); bool SCULPT_poll(struct bContext *C); bool SCULPT_poll_view3d(struct bContext *C); -bool SCULPT_vertex_colors_poll(struct bContext *C); +/** + * Returns true if sculpt session can handle color attributes + * (BKE_pbvh_type(ss->pbvh) == PBVH_FACES). If false an error + * message will be shown to the user. Operators should return + * OPERATOR_CANCELLED in this case. + * + * NOTE: Does not check if a color attribute actually exists. + * Calling code must handle this itself; in most cases a call to + * BKE_sculpt_color_layer_create_if_needed() is sufficient. + */ +bool SCULPT_handles_colors_report(struct SculptSession *ss, struct ReportList *reports); /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 9581bf2071c..e02c1b1aac3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -245,7 +245,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist); ED_sculpt_undo_geometry_end(ob); - BKE_mesh_calc_normals(ob->data); + BKE_mesh_normals_tag_dirty(mesh); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); break; @@ -627,11 +627,11 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) Mesh *mesh = ob->data; - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); + const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); if (mloopcol_layer_n == -1) { return OPERATOR_CANCELLED; } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); + 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) { @@ -662,6 +662,21 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } +static bool sculpt_colors_poll(bContext *C) +{ + if (!SCULPT_mode_poll(C)) { + return false; + } + + Object *ob = CTX_data_active_object(C); + + if (!ob->sculpt || !ob->sculpt->pbvh || BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_FACES) { + return false; + } + + return SCULPT_has_colors(ob->sculpt); +} + static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) { /* identifiers */ @@ -670,7 +685,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_vertex_to_loop_colors"; /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = sculpt_colors_poll; ot->exec = vertex_to_loop_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -692,11 +707,11 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) Mesh *mesh = ob->data; - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL); + const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); if (mloopcol_layer_n == -1) { return OPERATOR_CANCELLED; } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n); + 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) { @@ -734,15 +749,13 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_loop_to_vertex_colors"; /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = sculpt_colors_poll; ot->exec = loop_to_vertex_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int sculpt_sample_color_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *UNUSED(e)) +static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Scene *scene = CTX_data_scene(C); @@ -752,11 +765,17 @@ static int sculpt_sample_color_invoke(bContext *C, int active_vertex = SCULPT_active_vertex_get(ss); float active_vertex_color[4]; - if (!SCULPT_has_colors(ss)) { + if (!SCULPT_handles_colors_report(ss, op->reports)) { return OPERATOR_CANCELLED; } - SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); + /* No color attribute? Set color to white. */ + if (!SCULPT_has_colors(ss)) { + copy_v4_fl(active_vertex_color, 1.0f); + } + else { + SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); + } float color_srgb[3]; copy_v3_v3(color_srgb, active_vertex_color); @@ -777,7 +796,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_sample_color_invoke; - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER; } @@ -1025,8 +1044,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - /* Color data is not available in Multires. */ - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + /* Color data is not available in multi-resolution or dynamic topology. */ + if (!SCULPT_handles_colors_report(ss, op->reports)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 1fc7551f545..df1ccc0fbe9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -330,7 +330,7 @@ static void do_paint_pixels(void *__restrict userdata, kernel_float4.init_brush_color(image_buffer); } else { - kernel_float4.init_brush_color(image_buffer); + kernel_byte4.init_brush_color(image_buffer); } for (const PackedPixelRow &pixel_row : tile_data.pixel_rows) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index eae90359dfd..5867dc558de 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -854,7 +854,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase if (tag_update) { Mesh *mesh = ob->data; - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); BKE_sculptsession_free_deformMats(ss); } diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index eda9f89b51c..80efaf4ed9f 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -744,7 +744,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) for (strip = nlt->strips.first, index = 1; strip; strip = strip->next, index++) { if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) { const float xminc = strip->start + text_margin_x; - const float xmaxc = strip->end + text_margin_x; + const float xmaxc = strip->end - text_margin_x; /* draw the visualization of the strip */ nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax); 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 2536496b50d..fee64da0459 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -135,7 +135,7 @@ static CustomDataType data_type_in_attribute_input_node(const CustomDataType typ case CD_PROP_COLOR: case CD_PROP_BOOL: return type; - case CD_MLOOPCOL: + case CD_PROP_BYTE_COLOR: return CD_PROP_COLOR; case CD_PROP_STRING: /* Unsupported currently. */ diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index f5f5a9e6f67..91a21527ac9 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -179,6 +179,8 @@ void NODE_OT_view_selected(wmOperatorType *ot) struct NodeViewMove { int mvalo[2]; int xmin, ymin, xmax, ymax; + /** Original Offset for cancel. */ + float xof_orig, yof_orig; }; static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) @@ -207,13 +209,24 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e case LEFTMOUSE: case MIDDLEMOUSE: - case RIGHTMOUSE: if (event->val == KM_RELEASE) { MEM_freeN(nvm); op->customdata = nullptr; return OPERATOR_FINISHED; } break; + case EVT_ESCKEY: + case RIGHTMOUSE: + snode->xof = nvm->xof_orig; + snode->yof = nvm->yof_orig; + ED_region_tag_redraw(region); + WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr); + WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr); + + MEM_freeN(nvm); + op->customdata = nullptr; + + return OPERATOR_CANCELLED; } return OPERATOR_RUNNING_MODAL; @@ -249,6 +262,9 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent * nvm->ymin = -(region->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad; nvm->ymax = (region->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad; + nvm->xof_orig = snode->xof; + nvm->yof_orig = snode->yof; + BKE_image_release_ibuf(ima, ibuf, lock); /* add modal handler */ diff --git a/source/blender/editors/space_sequencer/sequencer_channels_draw.c b/source/blender/editors/space_sequencer/sequencer_channels_draw.c index b4bb4e950f0..a777132e9fc 100644 --- a/source/blender/editors/space_sequencer/sequencer_channels_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_channels_draw.c @@ -347,6 +347,8 @@ void channel_draw_context_init(const bContext *C, void draw_channels(const bContext *C, ARegion *region) { + draw_background(); + Editing *ed = SEQ_editing_get(CTX_data_scene(C)); if (ed == NULL) { return; @@ -357,7 +359,6 @@ void draw_channels(const bContext *C, ARegion *region) UI_view2d_view_ortho(context.v2d); - draw_background(); draw_channel_headers(&context); UI_view2d_view_restore(C); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 8e5931b127a..95707f83d85 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -27,6 +27,7 @@ #include "SEQ_channels.h" #include "SEQ_iterator.h" +#include "SEQ_relations.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -1920,7 +1921,7 @@ static bool select_grouped_effect(SeqCollection *strips, Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && - ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { + SEQ_relation_is_effect_of_strip(seq, actseq)) { effects[seq->type] = true; } } diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c index f5c5c7af4b6..43c5e004040 100644 --- a/source/blender/editors/space_sequencer/sequencer_thumbnails.c +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -86,6 +86,11 @@ static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) return false; } + /* Handle is moved, but not for this strip. */ + if ((G.moving & G_TRANSFORM_SEQ) != 0 && (seq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0) { + return false; + } + return true; } @@ -123,26 +128,13 @@ static void seq_get_thumb_image_dimensions(Sequence *seq, } } -static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area) -{ - if (seq->start > view_area->xmin && seq->start < view_area->xmax) { - return seq->start; - } - - /* Drawing and caching both check to see if strip is in view area or not before calling this - * function so assuming strip/part of strip in view. */ - - int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step; - return ((no_invisible_thumbs - 1) * frame_step) + seq->start; -} - static void thumbnail_start_job(void *data, short *stop, short *UNUSED(do_update), float *UNUSED(progress)) { ThumbnailDrawJob *tj = data; - float start_frame, frame_step; + float frame_step; GHashIterator gh_iter; @@ -155,9 +147,8 @@ static void thumbnail_start_job(void *data, if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { seq_get_thumb_image_dimensions( val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, NULL, NULL); - start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); SEQ_render_thumbnails( - &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); + &tj->context, val->seq_dupli, seq_orig, frame_step, tj->view_area, stop); SEQ_relations_sequence_free_anim(val->seq_dupli); } BLI_ghashIterator_step(&gh_iter); @@ -172,7 +163,6 @@ static void thumbnail_start_job(void *data, if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { seq_get_thumb_image_dimensions( val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, tj->thumb_height, NULL, NULL); - start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); SEQ_relations_sequence_free_anim(val->seq_dupli); } @@ -458,40 +448,31 @@ void draw_seq_strip_thumbnail(View2D *v2d, upper_thumb_bound = seq->enddisp; } - float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur); + float timeline_frame = SEQ_render_thumbnail_first_frame_get(seq, thumb_width, &v2d->cur); float thumb_x_end; - while (thumb_x_start + thumb_width < v2d->cur.xmin) { - thumb_x_start += thumb_width; - } - - /* Ignore thumbs to the left of strip. */ - while (thumb_x_start + thumb_width < seq->startdisp) { - thumb_x_start += thumb_width; - } - GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq); /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent * flickering after zooming. */ if (!sequencer_thumbnail_v2d_is_navigating(C)) { - last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start); + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, timeline_frame); } /* Start drawing. */ - while (thumb_x_start < upper_thumb_bound) { - thumb_x_end = thumb_x_start + thumb_width; + while (timeline_frame < upper_thumb_bound) { + thumb_x_end = timeline_frame + thumb_width; clipped = false; /* Checks to make sure that thumbs are loaded only when in view and within the confines of the * strip. Some may not be required but better to have conditions for safety as x1 here is * point to start caching from and not drawing. */ - if (thumb_x_start > v2d->cur.xmax) { + if (timeline_frame > v2d->cur.xmax) { break; } /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ - if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) { - cut_off = seq->startdisp - thumb_x_start; + if (IN_RANGE_INCL(seq->startdisp, timeline_frame, thumb_x_end)) { + cut_off = seq->startdisp - timeline_frame; clipped = true; } @@ -499,7 +480,7 @@ void draw_seq_strip_thumbnail(View2D *v2d, if (thumb_x_end > (upper_thumb_bound)) { thumb_x_end = upper_thumb_bound; clipped = true; - if (thumb_x_end - thumb_x_start < 1) { + if (thumb_x_end - timeline_frame < 1) { break; } } @@ -508,14 +489,12 @@ void draw_seq_strip_thumbnail(View2D *v2d, float zoom_y = thumb_height / image_height; float cropx_min = (cut_off / pixelx) / (zoom_y / pixely); - float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely); - if (cropx_max == (thumb_x_end - thumb_x_start)) { + float cropx_max = ((thumb_x_end - timeline_frame) / pixelx) / (zoom_y / pixely); + if (cropx_max == (thumb_x_end - timeline_frame)) { cropx_max = cropx_max + 1; } BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1); - int timeline_frame = round_fl_to_int(thumb_x_start); - /* Get the image. */ ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped); @@ -529,7 +508,7 @@ void draw_seq_strip_thumbnail(View2D *v2d, else if (!sequencer_thumbnail_v2d_is_navigating(C)) { /* Clear images in frame range occupied by new thumbnail. */ last_displayed_thumbnails_list_cleanup( - last_displayed_thumbnails, thumb_x_start, thumb_x_end); + last_displayed_thumbnails, timeline_frame, thumb_x_end); /* Insert new thumbnail frame to list. */ BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame)); } @@ -558,10 +537,10 @@ void draw_seq_strip_thumbnail(View2D *v2d, ED_draw_imbuf_ctx_clipping(C, ibuf, - thumb_x_start + cut_off, + timeline_frame + cut_off, y1, true, - thumb_x_start + cut_off, + timeline_frame + cut_off, y1, thumb_x_end, thumb_y_end, @@ -570,7 +549,7 @@ void draw_seq_strip_thumbnail(View2D *v2d, IMB_freeImBuf(ibuf); GPU_blend(GPU_BLEND_NONE); cut_off = 0; - thumb_x_start += thumb_width; + timeline_frame = SEQ_render_thumbnail_next_frame_get(seq, timeline_frame, thumb_width); } - last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX); + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, timeline_frame, FLT_MAX); } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 4d245b9ddaa..c407dad623d 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -22,9 +22,11 @@ #include "RNA_define.h" +#include "SEQ_iterator.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" +#include "SEQ_transform.h" /* For menu, popup, icons, etc. */ #include "ED_anim_api.h" @@ -260,14 +262,30 @@ void SEQUENCER_OT_view_zoom_ratio(wmOperatorType *ot) /** \name Frame Selected Operator * \{ */ -static int sequencer_view_selected_exec(bContext *C, wmOperator *op) +static void seq_view_collection_rect_preview(Scene *scene, SeqCollection *strips, rctf *rect) +{ + float min[2], max[2]; + SEQ_image_transform_bounding_box_from_collection(scene, strips, true, min, max); + + rect->xmin = min[0]; + rect->xmax = max[0]; + rect->ymin = min[1]; + rect->ymax = max[1]; + + float minsize = min_ff(BLI_rctf_size_x(rect), BLI_rctf_size_y(rect)); + + /* If the size of the strip is smaller than a pixel, add padding to prevent division by zero. */ + if (minsize < 1.0f) { + BLI_rctf_pad(rect, 20.0f, 20.0f); + } + + /* Add padding. */ + BLI_rctf_scale(rect, 1.1f); +} + +static void seq_view_collection_rect_timeline(Scene *scene, SeqCollection *strips, rctf *rect) { - Scene *scene = CTX_data_scene(C); - View2D *v2d = UI_view2d_fromcontext(C); - ARegion *region = CTX_wm_region(C); - Editing *ed = SEQ_editing_get(scene); Sequence *seq; - rctf cur_new = v2d->cur; int xmin = MAXFRAME * 2; int xmax = -MAXFRAME * 2; @@ -278,49 +296,63 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) int ymargin = 1; int xmargin = FPS; - if (ed == NULL) { - return OPERATOR_CANCELLED; - } - - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if (seq->flag & SELECT) { - xmin = min_ii(xmin, seq->startdisp); - xmax = max_ii(xmax, seq->enddisp); + SEQ_ITERATOR_FOREACH (seq, strips) { + xmin = min_ii(xmin, seq->startdisp); + xmax = max_ii(xmax, seq->enddisp); - ymin = min_ii(ymin, seq->machine); - ymax = max_ii(ymax, seq->machine); - } + ymin = min_ii(ymin, seq->machine); + ymax = max_ii(ymax, seq->machine); } - if (ymax != 0) { - const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + xmax += xmargin; + xmin -= xmargin; + ymax += ymargin; + ymin -= ymargin; - xmax += xmargin; - xmin -= xmargin; - ymax += ymargin; - ymin -= ymargin; + orig_height = BLI_rctf_size_y(rect); - orig_height = BLI_rctf_size_y(&cur_new); + rect->xmin = xmin; + rect->xmax = xmax; - cur_new.xmin = xmin; - cur_new.xmax = xmax; + rect->ymin = ymin; + rect->ymax = ymax; - cur_new.ymin = ymin; - cur_new.ymax = ymax; + /* Only zoom out vertically. */ + if (orig_height > BLI_rctf_size_y(rect)) { + ymid = BLI_rctf_cent_y(rect); - /* Only zoom out vertically. */ - if (orig_height > BLI_rctf_size_y(&cur_new)) { - ymid = BLI_rctf_cent_y(&cur_new); + rect->ymin = ymid - (orig_height / 2); + rect->ymax = ymid + (orig_height / 2); + } +} - cur_new.ymin = ymid - (orig_height / 2); - cur_new.ymax = ymid + (orig_height / 2); - } +static int sequencer_view_selected_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ARegion *region = CTX_wm_region(C); + SeqCollection *strips = selected_strips_from_context(C); + View2D *v2d = UI_view2d_fromcontext(C); + rctf cur_new = v2d->cur; - UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx); + if (SEQ_collection_len(strips) == 0) { + return OPERATOR_CANCELLED; + } - return OPERATOR_FINISHED; + if (sequencer_view_has_preview_poll(C) && !sequencer_view_preview_only_poll(C)) { + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + + if (region && region->regiontype == RGN_TYPE_PREVIEW) { + seq_view_collection_rect_preview(scene, strips, &cur_new); + } + else { + seq_view_collection_rect_timeline(scene, strips, &cur_new); + } + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + UI_view2d_smooth_view(C, region, &cur_new, smooth_viewtx); + + return OPERATOR_FINISHED; } void SEQUENCER_OT_view_selected(wmOperatorType *ot) diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 0a0669e02e4..89bff839481 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -331,6 +331,31 @@ static void sequencer_refresh(const bContext *C, ScrArea *area) break; } + ARegion *region_channels = sequencer_find_region(area, RGN_TYPE_CHANNELS); + if (sseq->view == SEQ_VIEW_SEQUENCE) { + 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; + } + } + if (view_changed) { ED_area_init(wm, window, area); ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 14b9dbe4b44..b3d6c395e89 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -300,6 +300,7 @@ static float get_default_column_width(const ColumnValues &values) return values.default_width; } static const float float_width = 3; + static const float int_width = 2; switch (values.type()) { case SPREADSHEET_VALUE_TYPE_BOOL: return 2.0f; @@ -317,6 +318,8 @@ static float get_default_column_width(const ColumnValues &values) return 8.0f; case SPREADSHEET_VALUE_TYPE_STRING: return 5.0f; + case SPREADSHEET_VALUE_TYPE_BYTE_COLOR: + return 4.0f * int_width; case SPREADSHEET_VALUE_TYPE_UNKNOWN: return 2.0f; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index 19fe61f0ed3..a29aa1fd026 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -44,6 +44,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type) if (type.is<InstanceReference>()) { return SPREADSHEET_VALUE_TYPE_INSTANCES; } + if (type.is<ColorGeometry4b>()) { + return SPREADSHEET_VALUE_TYPE_BYTE_COLOR; + } return SPREADSHEET_VALUE_TYPE_UNKNOWN; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index db466f8ccf3..e19a343335a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -191,6 +191,10 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index); this->draw_float_vector(params, Span(&value.r, 4)); } + else if (data.type().is<ColorGeometry4b>()) { + const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index); + this->draw_int_vector(params, {value.r, value.g, value.b, value.a}); + } else if (data.type().is<InstanceReference>()) { const InstanceReference value = data.get<InstanceReference>(real_index); switch (value.type()) { @@ -304,6 +308,34 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { } } + void draw_int_vector(const CellDrawParams ¶ms, const Span<int> values) const + { + BLI_assert(!values.is_empty()); + const float segment_width = (float)params.width / values.size(); + for (const int i : values.index_range()) { + const int value = values[i]; + const std::string value_str = std::to_string(value); + uiBut *but = uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + value_str.c_str(), + params.xmin + i * segment_width, + params.ymin, + segment_width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + /* Right-align Ints. */ + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); + } + } + int column_width(int column_index) const final { return spreadsheet_layout_.columns[column_index].width; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index e45317c2a5c..eb8f111baa0 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -106,10 +106,10 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, const float2 value = row_filter.value_float2; switch (row_filter.operation) { case SPREADSHEET_ROW_FILTER_EQUAL: { - const float threshold_sq = row_filter.threshold; + const float threshold_sq = pow2f(row_filter.threshold); apply_filter_operation( column_data.typed<float2>(), - [&](const float2 cell) { return math::distance_squared(cell, value) > threshold_sq; }, + [&](const float2 cell) { return math::distance_squared(cell, value) <= threshold_sq; }, prev_mask, new_indices); break; @@ -136,10 +136,10 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, const float3 value = row_filter.value_float3; switch (row_filter.operation) { case SPREADSHEET_ROW_FILTER_EQUAL: { - const float threshold_sq = row_filter.threshold; + const float threshold_sq = pow2f(row_filter.threshold); apply_filter_operation( column_data.typed<float3>(), - [&](const float3 cell) { return math::distance_squared(cell, value) > threshold_sq; }, + [&](const float3 cell) { return math::distance_squared(cell, value) <= threshold_sq; }, prev_mask, new_indices); break; @@ -168,19 +168,25 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } else if (column_data.type().is<ColorGeometry4f>()) { const ColorGeometry4f value = row_filter.value_color; - switch (row_filter.operation) { - case SPREADSHEET_ROW_FILTER_EQUAL: { - const float threshold_sq = row_filter.threshold; - apply_filter_operation( - column_data.typed<ColorGeometry4f>(), - [&](const ColorGeometry4f cell) { - return len_squared_v4v4(cell, value) > threshold_sq; - }, - prev_mask, - new_indices); - break; - } - } + const float threshold_sq = pow2f(row_filter.threshold); + apply_filter_operation( + column_data.typed<ColorGeometry4f>(), + [&](const ColorGeometry4f cell) { return len_squared_v4v4(cell, value) <= threshold_sq; }, + prev_mask, + new_indices); + } + else if (column_data.type().is<ColorGeometry4b>()) { + const ColorGeometry4b value = row_filter.value_byte_color; + const float4 value_floats = {(float)value.r, (float)value.g, (float)value.b, (float)value.a}; + const float threshold_sq = pow2f(row_filter.threshold); + apply_filter_operation( + column_data.typed<ColorGeometry4b>(), + [&](const ColorGeometry4b cell) { + const float4 cell_floats = {(float)cell.r, (float)cell.g, (float)cell.b, (float)cell.a}; + return len_squared_v4v4(value_floats, cell_floats) <= threshold_sq; + }, + prev_mask, + new_indices); } else if (column_data.type().is<InstanceReference>()) { const StringRef value = row_filter.value_string; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 6206b2d0c03..7c1ac024c12 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -42,7 +42,8 @@ static std::string operation_string(const eSpreadsheetColumnValueType data_type, if (ELEM(data_type, SPREADSHEET_VALUE_TYPE_BOOL, SPREADSHEET_VALUE_TYPE_INSTANCES, - SPREADSHEET_VALUE_TYPE_COLOR)) { + SPREADSHEET_VALUE_TYPE_COLOR, + SPREADSHEET_VALUE_TYPE_BYTE_COLOR)) { return "="; } @@ -101,6 +102,14 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter, } case SPREADSHEET_VALUE_TYPE_STRING: return row_filter.value_string; + case SPREADSHEET_VALUE_TYPE_BYTE_COLOR: { + std::ostringstream result; + result.precision(3); + result << std::fixed << "(" << (int)row_filter.value_byte_color[0] << ", " + << (int)row_filter.value_byte_color[1] << ", " << (int)row_filter.value_byte_color[2] + << ", " << (int)row_filter.value_byte_color[3] << ")"; + return result.str(); + } case SPREADSHEET_VALUE_TYPE_UNKNOWN: return ""; } @@ -229,6 +238,10 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel) case SPREADSHEET_VALUE_TYPE_STRING: uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE); break; + case SPREADSHEET_VALUE_TYPE_BYTE_COLOR: + uiItemR(layout, filter_ptr, "value_byte_color", 0, IFACE_("Value"), ICON_NONE); + uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); + break; case SPREADSHEET_VALUE_TYPE_UNKNOWN: uiItemL(layout, IFACE_("Unknown column type"), ICON_ERROR); break; diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index 4048e181fde..47d0168195b 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -31,26 +31,40 @@ static int txtfmt_py_find_builtinfunc(const char *string) { int i, len; - /* list is from... + /** + * The following items are derived from this list: + * \code{.py} * ", ".join(['"%s"' % kw - * for kw in __import__("keyword").kwlist - * if kw not in {"False", "None", "True", "def", "class"}]) + * for kw in sorted(__import__("keyword").kwlist + __import__("keyword").softkwlist) + * if kw not in {"False", "None", "True", "def", "class", "_"}]) + * \endcode * - * ... and for this code: - * print("\n".join(['else if (STR_LITERAL_STARTSWITH(string, "%s", len)) i = len;' % kw - * for kw in __import__("keyword").kwlist - * if kw not in {"False", "None", "True", "def", "class"}])) + * The code below can be re-generated using: + * \code{.py} + * import keyword + * ignore = {"False", "None", "True", "def", "class", "_"} + * keywords = sorted(set(keyword.kwlist + keyword.softkwlist) - ignore) + * longest = max(len(kw) for kw in keywords) + * first = 'if (STR_LITERAL_STARTSWITH(string, "%s",%s len)) { i = len;' + * middle = '} else if (STR_LITERAL_STARTSWITH(string, "%s",%s len)) { i = len;' + * last = '} else %s { i = 0;' + * print("\n".join([(first if i==0 else middle) % (kw, ' '*(longest - len(kw))) + * for (i, kw) in enumerate(keywords)]) + "\n" + + * last % (' '*(longest-2)) + "\n" + + * "}") + * \endcode */ /* Keep aligned args for readability. */ /* clang-format off */ - if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; + if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "continue", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "del", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "elif", len)) { i = len; @@ -65,6 +79,7 @@ static int txtfmt_py_find_builtinfunc(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "in", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "is", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "lambda", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "match", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "nonlocal", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "not", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "or", len)) { i = len; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 3f1483bbd03..753f82e483e 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3459,12 +3459,6 @@ static int text_insert_exec(bContext *C, wmOperator *op) while (str[i]) { code = BLI_str_utf8_as_unicode_step(str, str_len, &i); done |= txt_add_char(text, code); - if (U.text_flag & USER_TEXT_EDIT_AUTO_CLOSE) { - if (text_closing_character_pair_get(code)) { - done |= txt_add_char(text, text_closing_character_pair_get(code)); - txt_move_left(text, false); - } - } } } @@ -3484,6 +3478,7 @@ static int text_insert_exec(bContext *C, wmOperator *op) static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + uint auto_close_char = 0; int ret; /* NOTE: the "text" property is always set from key-map, @@ -3510,10 +3505,23 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) } str[len] = '\0'; RNA_string_set(op->ptr, "text", str); + + if (U.text_flag & USER_TEXT_EDIT_AUTO_CLOSE) { + auto_close_char = BLI_str_utf8_as_unicode(str); + } } ret = text_insert_exec(C, op); + if ((ret == OPERATOR_FINISHED) && (auto_close_char != 0)) { + const uint auto_close_match = text_closing_character_pair_get(auto_close_char); + if (auto_close_match != 0) { + Text *text = CTX_data_edit_text(C); + txt_add_char(text, auto_close_match); + txt_move_left(text, false); + } + } + /* run the script while editing, evil but useful */ if (ret == OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit) { text_run_script(C, NULL); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 6cb3d629e55..1d22c2f237b 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1628,7 +1628,7 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); break; case CTX_MODE_SCULPT_CURVES: - ARRAY_SET_ITEMS(contexts, ".curves_sculpt"); + ARRAY_SET_ITEMS(contexts, ".paint_common", ".curves_sculpt"); break; default: break; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 593c4f6e755..5a27349dc7f 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2371,7 +2371,7 @@ void ED_view3d_datamask(const bContext *C, CustomData_MeshMasks *r_cddata_masks) { if (ELEM(v3d->shading.type, OB_TEXTURE, OB_MATERIAL, OB_RENDER)) { - r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL; + r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR; r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR; } else if (v3d->shading.type == OB_SOLID) { @@ -2379,7 +2379,7 @@ void ED_view3d_datamask(const bContext *C, r_cddata_masks->lmask |= CD_MASK_MLOOPUV; } if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) { - r_cddata_masks->lmask |= CD_MASK_MLOOPCOL; + r_cddata_masks->lmask |= CD_MASK_PROP_BYTE_COLOR; r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR; } } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 05d4372723b..298de9b8730 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -737,6 +737,10 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv WM_event_drag_start_mval(event, ipd->region, mval); int flag_orig = snap_state_new->flag; snap_state_new->flag |= V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE; + + /* Be sure to also compute the #V3DSnapCursorData.plane_omat. */ + snap_state->draw_plane = true; + ED_view3d_cursor_snap_data_get(snap_state_new, C, mval[0], mval[1]); snap_state_new->flag = flag_orig; } diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index f2fb5b26305..838b40c2040 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -251,17 +251,9 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min SEQ_filter_selected_strips(strips); int selected_strips = SEQ_collection_len(strips); if (selected_strips > 0) { - INIT_MINMAX2(r_min, r_max); has_select = true; - - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips) { - float quad[4][2]; - SEQ_image_transform_quad_get(scene, seq, selected_strips != 1, quad); - for (int i = 0; i < 4; i++) { - minmax_v2v2_v2(r_min, r_max, quad[i]); - } - } + SEQ_image_transform_bounding_box_from_collection( + scene, strips, selected_strips != 1, r_min, r_max); } SEQ_collection_free(strips); if (selected_strips > 1) { diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index 348f012e300..acc6b20810f 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -106,7 +106,7 @@ static void transdata_elem_bend(const TransInfo *t, } if (t->options & CTX_GPENCIL_STROKES) { - /* grease pencil multiframe falloff */ + /* Grease pencil multi-frame falloff. */ bGPDstroke *gps = (bGPDstroke *)td->extra; if (gps != NULL) { fac_scaled = fac * td->factor * gps->runtime.multi_frame_falloff; diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index ab78ef6a5aa..abeb376a9a6 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -242,7 +242,7 @@ static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object * if (sod == nullptr) { if (sctx->cache.data_to_object_map != nullptr) { ob_eval = static_cast<Object *>( - BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data)); + BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->runtime.data_orig)); /* Could be NULl when mixing edit-mode and non edit-mode objects. */ if (ob_eval != nullptr) { sod = static_cast<SnapObjectData *>(BLI_ghash_lookup(sctx->cache.object_map, ob_eval)); @@ -369,7 +369,7 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx, sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__); } void **ob_p; - if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) { + if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->runtime.data_orig, &ob_p)) { ob_eval = static_cast<Object *>(*ob_p); } else { diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index cf229c9e9ec..7715388bf52 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -21,6 +21,7 @@ #include "SEQ_channels.h" #include "SEQ_effects.h" #include "SEQ_iterator.h" +#include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" @@ -102,8 +103,7 @@ static void query_strip_effects_fn(Sequence *seq_reference, /* Find all strips connected to `seq_reference`. */ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { - if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference || - seq_test->seq3 == seq_reference) { + if (SEQ_relation_is_effect_of_strip(seq_test, seq_reference)) { query_strip_effects_fn(seq_test, seqbase, collection); } } diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 54a6bb44bad..03e9134c6c2 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -608,9 +608,9 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex) // colors and transparency (the latter represented by grayscale colors) MLoopCol *colors = (MLoopCol *)CustomData_add_layer_named( - &mesh->ldata, CD_MLOOPCOL, CD_CALLOC, nullptr, mesh->totloop, "Color"); + &mesh->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, mesh->totloop, "Color"); MLoopCol *transp = (MLoopCol *)CustomData_add_layer_named( - &mesh->ldata, CD_MLOOPCOL, CD_CALLOC, nullptr, mesh->totloop, "Alpha"); + &mesh->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, mesh->totloop, "Alpha"); mesh->mloopcol = colors; mesh->mat = (Material **)MEM_mallocN(sizeof(Material *) * mesh->totcol, "MaterialList"); diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 5a27cda0787..0005d788a3b 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -38,7 +38,7 @@ #include "BLI_vector.hh" #include "BLI_vector_set.hh" -#include "FN_multi_function_builder.hh" +#include "FN_multi_function.hh" namespace blender::fn { diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index a2adc2ea8b6..5c43fffdd61 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -11,6 +11,7 @@ MAKE_FIELD_CPP_TYPE(FloatField, float); MAKE_FIELD_CPP_TYPE(Float2Field, blender::float2); MAKE_FIELD_CPP_TYPE(Float3Field, blender::float3); MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); +MAKE_FIELD_CPP_TYPE(ColorGeometry4bField, blender::ColorGeometry4b); MAKE_FIELD_CPP_TYPE(BoolField, bool); MAKE_FIELD_CPP_TYPE(Int8Field, int8_t); MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 944674c23a9..a53da717606 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -8,6 +8,7 @@ #include "BLI_vector_set.hh" #include "FN_field.hh" +#include "FN_multi_function_builder.hh" #include "FN_multi_function_procedure.hh" #include "FN_multi_function_procedure_builder.hh" #include "FN_multi_function_procedure_executor.hh" diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc index 7a0b74d8039..c58ca0bb8fd 100644 --- a/source/blender/functions/intern/multi_function_procedure_executor.cc +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -681,14 +681,9 @@ class VariableState : NonCopyable, NonMovable { /* Sanity check to make sure that enough indices can be destructed. */ BLI_assert(new_tot_initialized >= 0); - bool do_destruct_self = false; - switch (value_->type) { case ValueType::GVArray: { - if (mask.size() == full_mask.size()) { - do_destruct_self = true; - } - else { + if (mask.size() < full_mask.size()) { /* Not all elements are destructed. Since we can't work on the original array, we have to * create a copy first. */ this->ensure_is_mutable(full_mask, data_type, value_allocator); @@ -701,16 +696,10 @@ class VariableState : NonCopyable, NonMovable { case ValueType::Span: { const CPPType &type = data_type.single_type(); type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask); - if (new_tot_initialized == 0) { - do_destruct_self = true; - } break; } case ValueType::GVVectorArray: { - if (mask.size() == full_mask.size()) { - do_destruct_self = true; - } - else { + if (mask.size() < full_mask.size()) { /* Not all elements are cleared. Since we can't work on the original vector array, we * have to create a copy first. A possible future optimization is to create the partial * copy directly. */ @@ -729,22 +718,26 @@ class VariableState : NonCopyable, NonMovable { BLI_assert(value_typed->is_initialized); UNUSED_VARS_NDEBUG(value_typed); if (mask.size() == tot_initialized_) { - do_destruct_self = true; + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + value_typed->is_initialized = false; } break; } case ValueType::OneVector: { auto *value_typed = this->value_as<VariableValue_OneVector>(); - UNUSED_VARS(value_typed); if (mask.size() == tot_initialized_) { - do_destruct_self = true; + value_typed->data.clear(IndexRange(1)); } break; } } tot_initialized_ = new_tot_initialized; - return do_destruct_self; + + const bool should_self_destruct = new_tot_initialized == 0 && + caller_provided_storage_ == nullptr; + return should_self_destruct; } void indices_split(IndexMask mask, IndicesSplitVectors &r_indices) diff --git a/source/blender/functions/tests/FN_multi_function_procedure_test.cc b/source/blender/functions/tests/FN_multi_function_procedure_test.cc index a196d0396ec..e7cedb40f83 100644 --- a/source/blender/functions/tests/FN_multi_function_procedure_test.cc +++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc @@ -378,4 +378,34 @@ TEST(multi_function_procedure, BufferReuse) EXPECT_EQ(results[4], 53); } +TEST(multi_function_procedure, OutputBufferReplaced) +{ + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + const int output_value = 42; + CustomMF_GenericConstant constant_fn(CPPType::get<int>(), &output_value, false); + MFVariable &var_o = procedure.new_variable(MFDataType::ForSingle<int>()); + builder.add_output_parameter(var_o); + builder.add_call_with_all_variables(constant_fn, {&var_o}); + builder.add_destruct(var_o); + builder.add_call_with_all_variables(constant_fn, {&var_o}); + builder.add_return(); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{procedure}; + + Array<int> output(3, 0); + fn::MFParamsBuilder params(procedure_fn, output.size()); + params.add_uninitialized_single_output(output.as_mutable_span()); + + fn::MFContextBuilder context; + procedure_fn.call(IndexMask(output.size()), params, context); + + EXPECT_EQ(output[0], output_value); + EXPECT_EQ(output[1], output_value); + EXPECT_EQ(output[2], output_value); +} + } // namespace blender::fn::tests diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 9bb1cbb324e..e45b84632ab 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -1580,10 +1580,6 @@ static Mesh *create_merged_mesh(const Mesh &mesh, BLI_assert((int)r_i == result_npolys); BLI_assert(loop_cur == result_nloops); - /* We could only update the normals of the elements in context, but the next modifier can make it - * dirty anyway which would make the work useless. */ - BKE_mesh_normals_tag_dirty(result); - return result; } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 0a0f01c1ff2..f3f0e5b1fce 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1015,8 +1015,6 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, if (vertex_ids) { vertex_ids.save(); } - - BKE_mesh_normals_tag_dirty(dst_mesh); } /** \} */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index e2cd1741e16..b666eb677eb 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -984,7 +984,7 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) /* 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 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)) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4969d84fb54..57952887d89 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -370,7 +370,6 @@ set(GLSL_SRC shaders/material/gpu_shader_material_velvet.glsl shaders/material/gpu_shader_material_vertex_color.glsl shaders/material/gpu_shader_material_volume_absorption.glsl - shaders/material/gpu_shader_material_volume_info.glsl shaders/material/gpu_shader_material_volume_principled.glsl shaders/material/gpu_shader_material_volume_scatter.glsl shaders/material/gpu_shader_material_wireframe.glsl diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index f38b9681ad7..58bbe11b4d6 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -109,14 +109,14 @@ typedef enum eGPUMaterialStatus { GPU_MAT_SUCCESS, } eGPUMaterialStatus; -typedef enum eGPUVolumeDefaultValue { - GPU_VOLUME_DEFAULT_0, - GPU_VOLUME_DEFAULT_1, -} eGPUVolumeDefaultValue; +typedef enum eGPUDefaultValue { + GPU_DEFAULT_0 = 0, + GPU_DEFAULT_1, +} eGPUDefaultValue; typedef struct GPUCodegenOutput { char *attr_load; - /* Nodetree functions calls. */ + /* Node-tree functions calls. */ char *displacement; char *surface; char *volume; @@ -131,6 +131,10 @@ typedef void (*GPUCodegenCallbackFn)(void *thunk, GPUMaterial *mat, GPUCodegenOu GPUNodeLink *GPU_constant(const float *num); GPUNodeLink *GPU_uniform(const float *num); GPUNodeLink *GPU_attribute(GPUMaterial *mat, CustomDataType type, const char *name); +GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat, + CustomDataType type, + const char *name, + eGPUDefaultValue default_value); GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli); GPUNodeLink *GPU_image(GPUMaterial *mat, struct Image *ima, @@ -142,9 +146,7 @@ GPUNodeLink *GPU_image_tiled(GPUMaterial *mat, eGPUSamplerState sampler_state); GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser); GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *row); -GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, - const char *name, - eGPUVolumeDefaultValue default_value); + /** * Create an implementation defined differential calculation of a float function. * The given function should return a float. @@ -237,7 +239,6 @@ struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); -bool GPU_material_is_volume_shader(GPUMaterial *mat); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); @@ -255,9 +256,11 @@ void GPU_pass_cache_free(void); typedef struct GPUMaterialAttribute { struct GPUMaterialAttribute *next, *prev; - int type; /* CustomDataType */ - char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ + int type; /* CustomDataType */ + char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ + char input_name[12 + 1]; /* GPU_MAX_SAFE_ATTR_NAME + 1 */ eGPUType gputype; + eGPUDefaultValue default_value; /* Only for volumes attributes. */ int id; int users; } GPUMaterialAttribute; @@ -274,18 +277,8 @@ typedef struct GPUMaterialTexture { int sampler_state; /* eGPUSamplerState */ } GPUMaterialTexture; -typedef struct GPUMaterialVolumeGrid { - struct GPUMaterialVolumeGrid *next, *prev; - char *name; - eGPUVolumeDefaultValue default_value; - char sampler_name[32]; /* Name of sampler in GLSL. */ - char transform_name[32]; /* Name of 4x4 matrix in GLSL. */ - int users; -} GPUMaterialVolumeGrid; - ListBase GPU_material_attributes(GPUMaterial *material); ListBase GPU_material_textures(GPUMaterial *material); -ListBase GPU_material_volume_grids(GPUMaterial *material); typedef struct GPUUniformAttr { struct GPUUniformAttr *next, *prev; diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index eed7685bf01..0e7ce0889c2 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -371,12 +371,6 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, eGPUShaderConfig sh_cfg); GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader); -void GPU_shader_get_builtin_shader_code(eGPUBuiltinShader shader, - const char **r_vert, - const char **r_frag, - const char **r_geom, - const char **r_defines); - void GPU_shader_free_builtin_shaders(void); /* Vertex attributes for shaders */ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 110fdfbd2d9..fed998bb33e 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -207,7 +207,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, int update_flags) { const MPropCol *vtcol = vcol_type == CD_PROP_COLOR ? vcol_data : NULL; - const MLoopCol *vcol = vcol_type == CD_MLOOPCOL ? vcol_data : NULL; + const MLoopCol *vcol = vcol_type == CD_PROP_BYTE_COLOR ? vcol_data : NULL; const float(*f3col)[3] = vcol_type == CD_PROP_FLOAT3 ? vcol_data : NULL; const bool color_loops = vcol_domain == ATTR_DOMAIN_CORNER; diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index 8963fa45c96..048928b2312 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -49,7 +49,6 @@ using namespace blender::gpu::shader; struct GPUCodegenCreateInfo : ShaderCreateInfo { struct NameBuffer { - char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; char var_names[16][8]; }; @@ -171,10 +170,6 @@ static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) return stream << input->texture->sampler_name; case GPU_SOURCE_TEX_TILED_MAPPING: return stream << input->texture->tiled_mapping_name; - case GPU_SOURCE_VOLUME_GRID: - return stream << input->volume_grid->sampler_name; - case GPU_SOURCE_VOLUME_GRID_TRANSFORM: - return stream << input->volume_grid->transform_name; default: BLI_assert(0); return stream; @@ -276,28 +271,6 @@ class GPUCodegen { } }; -static char attr_prefix_get(CustomDataType type) -{ - switch (type) { - case CD_MTFACE: - return 'u'; - case CD_TANGENT: - return 't'; - case CD_MCOL: - case CD_MLOOPCOL: - return 'c'; - case CD_PROP_COLOR: - return 'c'; - case CD_AUTO_FROM_NAME: - return 'a'; - case CD_HAIRLENGTH: - return 'l'; - default: - BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); - return '\0'; - } -} - void GPUCodegen::generate_attribs() { if (BLI_listbase_is_empty(&graph.attributes)) { @@ -317,24 +290,9 @@ void GPUCodegen::generate_attribs() int slot = 15; LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { - - /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ - if (attr->type == CD_ORCO) { - /* OPTI: orco is computed from local positions, but only if no modifier is present. */ - STRNCPY(info.name_buffer->attr_names[slot], "orco"); - } - else { - char *name = info.name_buffer->attr_names[slot]; - name[0] = attr_prefix_get(static_cast<CustomDataType>(attr->type)); - name[1] = '\0'; - if (attr->name[0] != '\0') { - /* XXX FIXME: see notes in mesh_render_data_create() */ - GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME); - } - } SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); - blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; + blender::StringRefNull attr_name = attr->input_name; blender::StringRefNull var_name = info.name_buffer->var_names[slot]; eGPUType input_type, iface_type; @@ -395,12 +353,6 @@ void GPUCodegen::generate_resources() info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); } } - /* Volume Grids. */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) { - info.sampler(0, ImageType::FLOAT_3D, grid->sampler_name, Frequency::BATCH); - /* TODO(@fclem): Global uniform. To put in an UBO. */ - info.push_constant(Type::MAT4, grid->transform_name); - } if (!BLI_listbase_is_empty(&ubo_inputs_)) { /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */ diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 711a3943a25..3d5e9723103 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -219,11 +219,6 @@ ListBase GPU_material_textures(GPUMaterial *material) return material->graph.textures; } -ListBase GPU_material_volume_grids(GPUMaterial *material) -{ - return material->graph.volume_grids; -} - GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) { GPUUniformAttrList *attrs = &material->graph.uniform_attrs; @@ -599,11 +594,6 @@ void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status) /* Code generation */ -bool GPU_material_is_volume_shader(GPUMaterial *mat) -{ - return mat->is_volume_shader; -} - bool GPU_material_has_surface_output(GPUMaterial *mat) { return mat->has_surface_output; diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index d9143a12d5b..7f96a3b01c4 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -20,6 +20,7 @@ #include "BLI_utildefines.h" #include "GPU_texture.h" +#include "GPU_vertex_format.h" #include "gpu_material_library.h" #include "gpu_node_graph.h" @@ -102,14 +103,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->source = GPU_SOURCE_TEX_TILED_MAPPING; input->texture = link->texture; break; - case GPU_NODE_LINK_VOLUME_GRID: - input->source = GPU_SOURCE_VOLUME_GRID; - input->volume_grid = link->volume_grid; - break; - case GPU_NODE_LINK_VOLUME_GRID_TRANSFORM: - input->source = GPU_SOURCE_VOLUME_GRID_TRANSFORM; - input->volume_grid = link->volume_grid; - break; case GPU_NODE_LINK_ATTR: input->source = GPU_SOURCE_ATTR; input->attr = link->attr; @@ -335,6 +328,45 @@ void gpu_node_graph_finalize_uniform_attrs(GPUNodeGraph *graph) /* Attributes and Textures */ +static char attr_prefix_get(CustomDataType type) +{ + switch (type) { + case CD_MTFACE: + return 'u'; + case CD_TANGENT: + return 't'; + case CD_MCOL: + case CD_PROP_BYTE_COLOR: + return 'c'; + case CD_PROP_COLOR: + return 'c'; + case CD_AUTO_FROM_NAME: + return 'a'; + case CD_HAIRLENGTH: + return 'l'; + default: + BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); + return '\0'; + } +} + +static void attr_input_name(GPUMaterialAttribute *attr) +{ + /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + if (attr->type == CD_ORCO) { + /* OPTI: orco is computed from local positions, but only if no modifier is present. */ + STRNCPY(attr->input_name, "orco"); + } + else { + attr->input_name[0] = attr_prefix_get(attr->type); + attr->input_name[1] = '\0'; + if (attr->name[0] != '\0') { + /* XXX FIXME: see notes in mesh_render_data_create() */ + GPU_vertformat_safe_attr_name(attr->name, &attr->input_name[1], GPU_MAX_SAFE_ATTR_NAME); + } + } +} + /** Add a new varying attribute of given type and name. Returns NULL if out of slots. */ static GPUMaterialAttribute *gpu_node_graph_add_attribute(GPUNodeGraph *graph, CustomDataType type, @@ -360,6 +392,7 @@ static GPUMaterialAttribute *gpu_node_graph_add_attribute(GPUNodeGraph *graph, attr = MEM_callocN(sizeof(*attr), __func__); attr->type = type; STRNCPY(attr->name, name); + attr_input_name(attr); attr->id = num_attributes; BLI_addtail(&graph->attributes, attr); } @@ -443,35 +476,6 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph, return tex; } -static GPUMaterialVolumeGrid *gpu_node_graph_add_volume_grid(GPUNodeGraph *graph, - const char *name, - eGPUVolumeDefaultValue default_value) -{ - /* Find existing volume grid. */ - int num_grids = 0; - GPUMaterialVolumeGrid *grid = graph->volume_grids.first; - for (; grid; grid = grid->next) { - if (STREQ(grid->name, name) && grid->default_value == default_value) { - break; - } - num_grids++; - } - - /* Add new requested volume grid. */ - if (grid == NULL) { - grid = MEM_callocN(sizeof(*grid), __func__); - grid->name = BLI_strdup(name); - grid->default_value = default_value; - BLI_snprintf(grid->sampler_name, sizeof(grid->sampler_name), "vsamp%d", num_grids); - BLI_snprintf(grid->transform_name, sizeof(grid->transform_name), "vtfm%d", num_grids); - BLI_addtail(&graph->volume_grids, grid); - } - - grid->users++; - - return grid; -} - /* Creating Inputs */ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const char *name) @@ -496,6 +500,18 @@ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const ch return link; } +GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat, + const CustomDataType type, + const char *name, + eGPUDefaultValue default_value) +{ + GPUNodeLink *link = GPU_attribute(mat, type, name); + if (link->link_type == GPU_NODE_LINK_ATTR) { + link->attr->default_value = default_value; + } + return link; +} + GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli) { GPUNodeGraph *graph = gpu_material_node_graph(mat); @@ -586,39 +602,6 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro return link; } -GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, - const char *name, - eGPUVolumeDefaultValue default_value) -{ - /* NOTE: this could be optimized by automatically merging duplicate - * lookups of the same attribute. */ - GPUNodeGraph *graph = gpu_material_node_graph(mat); - GPUNodeLink *link = gpu_node_link_create(); - link->link_type = GPU_NODE_LINK_VOLUME_GRID; - link->volume_grid = gpu_node_graph_add_volume_grid(graph, name, default_value); - - GPUNodeLink *transform_link = gpu_node_link_create(); - transform_link->link_type = GPU_NODE_LINK_VOLUME_GRID_TRANSFORM; - transform_link->volume_grid = link->volume_grid; - transform_link->volume_grid->users++; - - GPUNodeLink *cos_link = GPU_attribute(mat, CD_ORCO, ""); - - /* Two special cases, where we adjust the output values of smoke grids to - * bring the into standard range without having to modify the grid values. */ - if (STREQ(name, "color")) { - GPU_link(mat, "node_attribute_volume_color", link, transform_link, cos_link, &link); - } - else if (STREQ(name, "temperature")) { - GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, cos_link, &link); - } - else { - GPU_link(mat, "node_attribute_volume", link, transform_link, cos_link, &link); - } - - return link; -} - /* Creating Nodes */ bool GPU_link(GPUMaterial *mat, const char *name, ...) @@ -767,9 +750,6 @@ static void gpu_inputs_free(ListBase *inputs) else if (ELEM(input->source, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING)) { input->texture->users--; } - else if (ELEM(input->source, GPU_SOURCE_VOLUME_GRID, GPU_SOURCE_VOLUME_GRID_TRANSFORM)) { - input->volume_grid->users--; - } if (input->link) { gpu_node_link_free(input->link); @@ -816,10 +796,6 @@ void gpu_node_graph_free(GPUNodeGraph *graph) BLI_freelistN(&graph->material_functions); gpu_node_graph_free_nodes(graph); - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { - MEM_SAFE_FREE(grid->name); - } - BLI_freelistN(&graph->volume_grids); BLI_freelistN(&graph->textures); BLI_freelistN(&graph->attributes); GPU_uniform_attr_list_free(&graph->uniform_attrs); @@ -894,14 +870,6 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph) } } - for (GPUMaterialVolumeGrid *grid = graph->volume_grids.first, *next = NULL; grid; grid = next) { - next = grid->next; - if (grid->users == 0) { - MEM_SAFE_FREE(grid->name); - BLI_freelinkN(&graph->volume_grids, grid); - } - } - GPUUniformAttrList *uattrs = &graph->uniform_attrs; LISTBASE_FOREACH_MUTABLE (GPUUniformAttr *, attr, &uattrs->list) { diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 024119e1c24..ae472d5b7aa 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -34,8 +34,6 @@ typedef enum eGPUDataSource { GPU_SOURCE_STRUCT, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, - GPU_SOURCE_VOLUME_GRID, - GPU_SOURCE_VOLUME_GRID_TRANSFORM, GPU_SOURCE_FUNCTION_CALL, } eGPUDataSource; @@ -48,8 +46,6 @@ typedef enum { GPU_NODE_LINK_IMAGE, GPU_NODE_LINK_IMAGE_TILED, GPU_NODE_LINK_IMAGE_TILED_MAPPING, - GPU_NODE_LINK_VOLUME_GRID, - GPU_NODE_LINK_VOLUME_GRID_TRANSFORM, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN, @@ -90,8 +86,6 @@ struct GPUNodeLink { const float *data; /* GPU_NODE_LINK_COLORBAND */ struct GPUTexture **colorband; - /* GPU_NODE_LINK_VOLUME_GRID */ - struct GPUMaterialVolumeGrid *volume_grid; /* GPU_NODE_LINK_OUTPUT */ struct GPUOutput *output; /* GPU_NODE_LINK_ATTR */ @@ -134,8 +128,6 @@ typedef struct GPUInput { struct GPUMaterialAttribute *attr; /* GPU_SOURCE_UNIFORM_ATTR */ struct GPUUniformAttr *uniform_attr; - /* GPU_SOURCE_VOLUME_GRID | GPU_SOURCE_VOLUME_GRID_TRANSFORM */ - struct GPUMaterialVolumeGrid *volume_grid; /* GPU_SOURCE_FUNCTION_CALL */ char function_call[64]; }; @@ -170,7 +162,6 @@ typedef struct GPUNodeGraph { /* Requested attributes and textures. */ ListBase attributes; ListBase textures; - ListBase volume_grids; /* The list of uniform attributes. */ GPUUniformAttrList uniform_attrs; diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index dfedb64ce61..1958ecedb28 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -402,19 +402,6 @@ GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader) return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT); } -void GPU_shader_get_builtin_shader_code(eGPUBuiltinShader shader, - const char **r_vert, - const char **r_frag, - const char **r_geom, - const char **r_defines) -{ - const GPUShaderStages *stages = &builtin_shader_stages[shader]; - *r_vert = stages->vert; - *r_frag = stages->frag; - *r_geom = stages->geom; - *r_defines = stages->defs; -} - void GPU_shader_free_builtin_shaders(void) { for (int i = 0; i < GPU_SHADER_CFG_LEN; i++) { diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 3ab96d0d84a..7c827e237eb 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -627,7 +627,9 @@ struct ShaderCreateInfo { Resource res(Resource::BindType::SAMPLER, slot); res.sampler.type = type; res.sampler.name = name; - res.sampler.sampler = sampler; + /* Produces ASAN errors for the moment. */ + // res.sampler.sampler = sampler; + UNUSED_VARS(sampler); ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res); interface_names_size_ += name.size() + 1; return *(Self *)this; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 9768318f3b6..51d470ef0c1 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -855,7 +855,7 @@ static char *glsl_patch_default_get() STR_CONCAT(patch, slen, "#define gpu_InstanceIndex (gl_InstanceID + gpu_BaseInstance)\n"); /* Array compat. */ - STR_CONCAT(patch, slen, "#define array(_type) _type[]\n"); + STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n"); /* Derivative sign can change depending on implementation. */ STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", GLContext::derivative_signs[0]); @@ -882,7 +882,7 @@ static char *glsl_patch_compute_get() STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); /* Array compat. */ - STR_CONCAT(patch, slen, "#define array(_type) _type[]\n"); + STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n"); BLI_assert(slen < sizeof(patch)); return patch; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl index faf37db3ea6..2ae53b35b3f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl @@ -1,3 +1,17 @@ + +void node_attribute_color(vec4 attr, out vec4 out_attr) +{ + out_attr = attr_load_color_post(attr); +} + +void node_attribute_temperature(vec4 attr, out vec4 out_attr) +{ + out_attr.x = attr_load_temperature_post(attr.x); + out_attr.y = 0.0; + out_attr.z = 0.0; + out_attr.w = 1.0; +} + void node_attribute( vec4 attr, out vec4 outcol, out vec3 outvec, out float outf, out float outalpha) { 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 2885bf4e082..a8b4b039370 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,6 +1,5 @@ #pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) - void node_hair_info(float hair_length, out float is_strand, out float intercept, 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 1b1fed9502e..ad3d4737193 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,6 +1,5 @@ #pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl) - void node_point_info(out vec3 position, out float radius, out float random) { #ifdef POINTCLOUD_SHADER 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 89091316823..da131978f72 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,6 +1,5 @@ #pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl) - void node_tex_environment_equirectangular(vec3 co, out vec3 uv) { vec3 nco = normalize(co); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl deleted file mode 100644 index 464cf5227b4..00000000000 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl +++ /dev/null @@ -1,51 +0,0 @@ - -/* Uniforms to convert smoke grid values into standard range. */ -uniform vec3 volumeColor = vec3(1.0); -uniform vec2 volumeTemperature = vec2(0.0); - -/* Generic volume attribute. */ -void node_attribute_volume(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec) -{ - /* Optional per-grid transform. */ - if (transform[3][3] != 0.0) { - cos = (transform * vec4(cos, 1.0)).xyz; - } - - outvec = texture(tex, cos).rgb; -} - -/* Special color attribute for smoke. */ -void node_attribute_volume_color(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec) -{ - /* Optional per-grid transform. */ - if (transform[3][3] != 0.0) { - cos = (transform * vec4(cos, 1.0)).xyz; - } - - /* Density is premultiplied for interpolation, divide it out here. */ - vec4 value = texture(tex, cos).rgba; - if (value.a > 1e-8) { - value.rgb /= value.a; - } - - outvec = value.rgb * volumeColor; -} - -/* Special temperature attribute for smoke. */ -void node_attribute_volume_temperature(sampler3D tex, mat4 transform, vec3 cos, out float outf) -{ - /* Optional per-grid transform. */ - if (transform[3][3] != 0.0) { - cos = (transform * vec4(cos, 1.0)).xyz; - } - - float value = texture(tex, cos).r; - if (volumeTemperature.x < volumeTemperature.y) { - outf = (value > 0.01) ? - volumeTemperature.x + value * (volumeTemperature.y - volumeTemperature.x) : - 0.0; - } - else { - outf = value; - } -} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl index 1127c34b3ac..21c4aba0ffe 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl @@ -10,9 +10,9 @@ void node_volume_principled(vec4 color, vec4 blackbody_tint, float temperature, float weight, - float density_attribute, + vec4 density_attribute, vec4 color_attribute, - float temperature_attribute, + vec4 temperature_attribute, sampler1DArray spectrummap, float layer, out Closure result) @@ -25,7 +25,7 @@ void node_volume_principled(vec4 color, density = max(density, 0.0); if (density > 1e-5) { - density = max(density * density_attribute, 0.0); + density = max(density * density_attribute.x, 0.0); } if (density > 1e-5) { @@ -47,7 +47,7 @@ void node_volume_principled(vec4 color, if (blackbody_intensity > 1e-3) { /* Add temperature from attribute. */ - float T = max(temperature * max(temperature_attribute, 0.0), 0.0); + float T = max(temperature * max(temperature_attribute.x, 0.0), 0.0); /* Stefan-Boltzman law. */ float T2 = T * T; diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index bbb196dc383..07b185ffd64 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -359,7 +359,7 @@ void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) else { arb_geom_params = abc_poly_mesh_.getSchema().getArbGeomParams(); } - write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL); + write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_PROP_BYTE_COLOR); } bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels) diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index c413fbf0ff9..45bf898f3f5 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -294,7 +294,7 @@ void write_custom_data(const OCompoundProperty &prop, write_uv(prop, config, cd_data, name); } - else if (cd_data_type == CD_MLOOPCOL) { + else if (cd_data_type == CD_PROP_BYTE_COLOR) { write_mcol(prop, config, cd_data, name); } } @@ -412,7 +412,7 @@ static void read_custom_data_mcols(const std::string &iobject_full_name, /* Read the vertex colors */ void *cd_data = config.add_customdata_cb( - config.mesh, prop_header.getName().c_str(), CD_MLOOPCOL); + config.mesh, prop_header.getName().c_str(), CD_PROP_BYTE_COLOR); MCol *cfaces = static_cast<MCol *>(cd_data); MPoly *mpolys = config.mpoly; MLoop *mloops = config.mloop; diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index fe2b0470432..2d2dcfb1f42 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -245,7 +245,7 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) static void process_no_normals(CDStreamConfig &config) { /* Absence of normals in the Alembic mesh is interpreted as 'smooth'. */ - BKE_mesh_calc_normals(config.mesh); + BKE_mesh_normals_tag_dirty(config.mesh); } static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr) @@ -382,7 +382,7 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) int numloops; /* unsupported custom data type -- don't do anything. */ - if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_PROP_BYTE_COLOR)) { return nullptr; } diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index 3b21d423df5..3952a4ae334 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -378,12 +378,12 @@ void GeometryExporter::create_mesh_primitive_list(short material_index, } } - int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR); if (totlayer_mcol > 0) { int map_index = 0; for (int a = 0; a < totlayer_mcol; a++) { - char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); + char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_PROP_BYTE_COLOR, a); COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR, makeUrl(makeVertexColorSourceId(geom_id, layer_name)), (has_uvs) ? 3 : 2, /* all color layers have same index order */ @@ -468,7 +468,7 @@ void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me) void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) { /* Find number of vertex color layers */ - int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR); if (totlayer_mcol == 0) { return; } @@ -477,11 +477,11 @@ 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_MLOOPCOL, a); + MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, a); COLLADASW::FloatSourceF source(mSW); - char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); + char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_PROP_BYTE_COLOR, a); std::string layer_id = makeVertexColorSourceId(geom_id, layer_name); source.setId(layer_id); diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index fc94e912417..6e109353be8 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -471,9 +471,9 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) collada_mesh->getColors().getInputInfosArray()[i]; COLLADAFW::String colname = extract_vcolname(info->mName); CustomData_add_layer_named( - &me->ldata, CD_MLOOPCOL, CD_DEFAULT, nullptr, me->totloop, colname.c_str()); + &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, colname.c_str()); } - me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, 0); + me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, 0); } } } @@ -724,7 +724,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) COLLADAFW::IndexList &color_index_list = *mp->getColorIndices(vcolor_index); COLLADAFW::String colname = extract_vcolname(color_index_list.getName()); MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named( - &me->ldata, CD_MLOOPCOL, colname.c_str()); + &me->ldata, CD_PROP_BYTE_COLOR, colname.c_str()); if (mloopcol == nullptr) { fprintf(stderr, "Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n", @@ -1057,7 +1057,6 @@ Object *MeshImporter::create_mesh_object( Mesh *new_mesh = uid_mesh_map[*geom_uid]; BKE_mesh_assign_object(m_bmain, ob, new_mesh); - BKE_mesh_calc_normals(new_mesh); /* Because BKE_mesh_assign_object would have already decreased it... */ id_us_plus(&old_mesh->id); diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/common/intern/string_utils.cc index 01107b0866e..3a12250e14b 100644 --- a/source/blender/io/common/intern/string_utils.cc +++ b/source/blender/io/common/intern/string_utils.cc @@ -7,7 +7,7 @@ * their standard libraries are not quite there yet. * LLVM/libc++ only has a float parser since LLVM 14, * and gcc/libstdc++ since 11.1. So until at least these are - * the mininum spec, use an external library. */ + * the minimum spec, use an external library. */ #include "fast_float.h" #include <charconv> diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 646d1ba1fde..328b4109616 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -149,7 +149,7 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type int numloops; /* unsupported custom data type -- don't do anything. */ - if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_PROP_BYTE_COLOR)) { return nullptr; } @@ -457,7 +457,7 @@ void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime) return; } - void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_MLOOPCOL); + void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_PROP_BYTE_COLOR); if (!cd_ptr) { std::cerr << "WARNING: Couldn't add displayColors custom data.\n"; diff --git a/source/blender/io/usd/tests/usd_tests_common.h b/source/blender/io/usd/tests/usd_tests_common.h index b298a253ddc..7f6ae558354 100644 --- a/source/blender/io/usd/tests/usd_tests_common.h +++ b/source/blender/io/usd/tests/usd_tests_common.h @@ -8,7 +8,7 @@ namespace blender::io::usd { /* Calls the function to load the USD plugins from the * USD data directory under the Blender bin directory - * that was supplied as the --test-release-dir flag to ctest. + * that was supplied as the --test-release-dir flag to `ctest`. * Thus function must be called before instantiating a USD * stage to avoid errors. The returned string is the path to * the USD data files directory from which the plugins were diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index ee78b610276..3e7a4431bf5 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -610,6 +610,8 @@ typedef enum eBrushFalloffShape { typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM = (1 << 0), 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), } 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 2d879f5afa0..7d230b7d7a3 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -144,6 +144,8 @@ typedef struct BrushCurvesSculptSettings { uint32_t flag; /** When shrinking curves, they shouldn't become shorter than this length. */ float minimum_length; + /** Length of newly added curves when it is not interpolated from other curves. */ + float curve_length; } BrushCurvesSculptSettings; typedef struct Brush { diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 305b913b19e..6e18d442ee2 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -298,6 +298,13 @@ typedef struct Curve { * original object data. */ const struct Curves *curve_eval; + /** + * If non-zero, the #editfont and #editnurb pointers are not owned by this #Curve. That means + * this curve is a container for the result of object geometry evaluation. This only works + * because evaluated object data never outlives original data. + */ + char edit_data_from_original; + char _pad3[7]; void *batch_cache; } Curve; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 858de733558..ef937fb139b 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -113,7 +113,7 @@ typedef enum CustomDataType { CD_MTEXPOLY = 15, /* deprecated */ #endif CD_MLOOPUV = 16, - CD_MLOOPCOL = 17, + CD_PROP_BYTE_COLOR = 17, CD_TANGENT = 18, CD_MDISPS = 19, CD_PREVIEW_MCOL = 20, /* for displaying weightpaint colors */ @@ -181,7 +181,7 @@ typedef enum CustomDataType { #define CD_MASK_ORCO (1 << CD_ORCO) // #define CD_MASK_MTEXPOLY (1 << CD_MTEXPOLY) /* DEPRECATED */ #define CD_MASK_MLOOPUV (1 << CD_MLOOPUV) -#define CD_MASK_MLOOPCOL (1 << CD_MLOOPCOL) +#define CD_MASK_PROP_BYTE_COLOR (1 << CD_PROP_BYTE_COLOR) #define CD_MASK_TANGENT (1 << CD_TANGENT) #define CD_MASK_MDISPS (1 << CD_MDISPS) #define CD_MASK_PREVIEW_MCOL (1 << CD_PREVIEW_MCOL) @@ -224,11 +224,11 @@ typedef enum CustomDataType { /* All generic attributes. */ #define CD_MASK_PROP_ALL \ (CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \ - CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL | \ + CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_BOOL | \ CD_MASK_PROP_INT8) /* All color attributes */ -#define CD_MASK_COLOR_ALL (CD_MASK_PROP_COLOR | CD_MASK_MLOOPCOL) +#define CD_MASK_COLOR_ALL (CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR) typedef struct CustomData_MeshMasks { uint64_t vmask; diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h index fd48585792f..90a91c6c995 100644 --- a/source/blender/makesdna/DNA_fluid_defaults.h +++ b/source/blender/makesdna/DNA_fluid_defaults.h @@ -187,6 +187,7 @@ .cache_comp = SM_CACHE_LIGHT, \ .cache_high_comp = SM_CACHE_LIGHT, \ .cache_file_format = 0, \ + .velocity_scale = 1.0f, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 11780d99af8..5a1636879bb 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -670,7 +670,10 @@ typedef struct FluidDomainSettings { char interp_method; char gridlines_color_field; /* Simulation field used to color map onto gridlines. */ char gridlines_cell_filter; - char _pad10[7]; /* Unused. */ + char _pad10[3]; /* Unused. */ + + /* Velocity factor for motion blur rendering. */ + float velocity_scale; /* OpenVDB cache options. */ int openvdb_compression; diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 2b4858d4106..cd4676ee08e 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -221,7 +221,7 @@ typedef struct Mesh { /** * The active vertex corner color layer, if it exists. Also called "Vertex Color" in Blender's * UI, even though it is stored per face corner. - * \note This pointer is for convenient access to the #CD_MLOOPCOL layer in #ldata. + * \note This pointer is for convenient access to the #CD_PROP_BYTE_COLOR layer in #ldata. */ struct MLoopCol *mloopcol; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index d6c1040110f..bfe967fcde5 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1014,23 +1014,8 @@ typedef struct Sculpt { struct Object *gravity_object; } Sculpt; -typedef enum CurvesSculptFlag { - CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 0), - CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 1), -} CurvesSculptFlag; - typedef struct CurvesSculpt { Paint paint; - /** Minimum distance between newly added curves on a surface. */ - float distance; - - /** CurvesSculptFlag. */ - uint32_t flag; - - /** Length of newly added curves when it is not interpolated from other curves. */ - float curve_length; - - char _pad[4]; } CurvesSculpt; typedef struct UvSculpt { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 8d527caa361..bce124a26cf 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1968,8 +1968,7 @@ typedef struct SpreadsheetRowFilter { float value_float2[2]; float value_float3[3]; float value_color[4]; - - char _pad1[4]; + uint8_t value_byte_color[4]; } SpreadsheetRowFilter; typedef enum eSpaceSpreadsheet_RowFilterFlag { @@ -2006,6 +2005,7 @@ typedef enum eSpreadsheetColumnValueType { SPREADSHEET_VALUE_TYPE_COLOR = 5, SPREADSHEET_VALUE_TYPE_INSTANCES = 6, SPREADSHEET_VALUE_TYPE_STRING = 7, + SPREADSHEET_VALUE_TYPE_BYTE_COLOR = 8, } eSpreadsheetColumnValueType; /** diff --git a/source/blender/makesdna/DNA_volume_defaults.h b/source/blender/makesdna/DNA_volume_defaults.h index 2025d664d40..ee98f0ea4fd 100644 --- a/source/blender/makesdna/DNA_volume_defaults.h +++ b/source/blender/makesdna/DNA_volume_defaults.h @@ -36,7 +36,8 @@ .frame_duration = 0, \ .display = _DNA_DEFAULT_VolumeDisplay, \ .render = _DNA_DEFAULT_VolumeRender, \ - } + .velocity_scale = 1.0f, \ +} /** \} */ diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h index f2f53bc910f..a2e558aa790 100644 --- a/source/blender/makesdna/DNA_volume_types.h +++ b/source/blender/makesdna/DNA_volume_types.h @@ -24,6 +24,11 @@ typedef struct Volume_Runtime { /** Default simplify level for volume grids loaded from files. */ int default_simplify_level; + + /* Names for scalar grids which would need to be merged to recompose the velocity grid. */ + char velocity_x_grid[64]; + char velocity_y_grid[64]; + char velocity_z_grid[64]; } Volume_Runtime; typedef struct VolumeDisplay { @@ -75,6 +80,18 @@ typedef struct Volume { VolumeRender render; VolumeDisplay display; + /* Velocity field name. */ + char velocity_grid[64]; + + char _pad3[3]; + + /* Unit of time the velocity vectors are expressed in. + * This uses the same enumeration values as #CacheFile.velocity_unit. */ + char velocity_unit; + + /* Factor for velocity vector for artistic control. */ + float velocity_scale; + /* Draw Cache */ void *batch_cache; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index ade0fcdb13f..cfd0c986df9 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -364,7 +364,7 @@ typedef struct wmKeyMapItem { short type; /** KM_ANY, KM_PRESS, KM_NOTHING etc. */ int8_t val; - /** Use when `val == KM_CLICK_DRAG`, */ + /** Use when `val == KM_CLICK_DRAG`. */ int8_t direction; /** `oskey` also known as apple, windows-key or super. */ short shift, ctrl, alt, oskey; diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 6e2c898d691..6539d636697 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -212,6 +212,8 @@ DEF_ENUM(rna_enum_subdivision_boundary_smooth_items) DEF_ENUM(rna_enum_transform_orientation_items) +DEF_ENUM(rna_enum_velocity_unit_items) + /* Not available to RNA pre-processing (`makesrna`). * Defined in editors for example. */ #ifndef RNA_MAKESRNA diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 8d022338d7a..df92601dd0c 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -1047,6 +1047,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Bone_bbone_handle_update"); } RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text( prop, "B-Bone Start Handle", "Bone that serves as the start handle for the B-Bone curve"); @@ -1091,6 +1092,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_update(prop, 0, "rna_Bone_bbone_handle_update"); } RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text( prop, "B-Bone End Handle", "Bone that serves as the end handle for the B-Bone curve"); diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index b11250cf891..5efbebc9b97 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -28,7 +28,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {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 with floating-point values"}, - {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, @@ -42,7 +42,7 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {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 with floating-point values"}, - {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, + {CD_PROP_BYTE_COLOR, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, @@ -113,7 +113,7 @@ static StructRNA *srna_by_custom_data_layer_type(const CustomDataType type) return &RNA_FloatVectorAttribute; case CD_PROP_COLOR: return &RNA_FloatColorAttribute; - case CD_MLOOPCOL: + case CD_PROP_BYTE_COLOR: return &RNA_ByteColorAttribute; case CD_PROP_STRING: return &RNA_StringAttribute; @@ -231,7 +231,7 @@ static void rna_Attribute_data_begin(CollectionPropertyIterator *iter, PointerRN case CD_PROP_COLOR: struct_size = sizeof(MPropCol); break; - case CD_MLOOPCOL: + case CD_PROP_BYTE_COLOR: struct_size = sizeof(MLoopCol); break; case CD_PROP_STRING: @@ -339,9 +339,17 @@ static int rna_Attributes_layer_skip(CollectionPropertyIterator *UNUSED(iter), v return !(CD_TYPE_AS_MASK(layer->type) & CD_MASK_PROP_ALL); } -static int rna_Attributes_noncolor_layer_skip(CollectionPropertyIterator *UNUSED(iter), void *data) +static int rna_Attributes_noncolor_layer_skip(CollectionPropertyIterator *iter, void *data) { CustomDataLayer *layer = (CustomDataLayer *)data; + + /* Check valid domain here, too, keep in line with rna_AttributeGroup_color_length(). */ + ID *id = iter->parent.owner_id; + AttributeDomain domain = BKE_id_attribute_domain(id, layer); + if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + return 1; + } + return !(CD_TYPE_AS_MASK(layer->type) & CD_MASK_COLOR_ALL) || (layer->flag & CD_FLAG_TEMPORARY); } @@ -423,7 +431,7 @@ int rna_AttributeGroup_color_length(PointerRNA *ptr) { return BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER, - CD_MASK_PROP_COLOR | CD_MASK_MLOOPCOL); + CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR); } int rna_AttributeGroup_length(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 25dd87b1e74..cc5141bde03 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -119,11 +119,11 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {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", ""}, + {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, {SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""}, {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""}, {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, - {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, {0, NULL, 0, NULL, NULL}, }; /* clang-format on */ @@ -1944,6 +1944,23 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Minimum Length", "Avoid shrinking curves shorter than this length"); + + prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); + RNA_def_property_ui_text( + prop, "Interpolate Length", "Use length of 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( + prop, "Interpolate Shape", "Use shape of the curves in close proximity"); + + prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text( + prop, + "Curve Length", + "Length of newly added curves when it is not interpolated from other curves"); } static void rna_def_brush(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index 62b08ebb281..c8b154b9b04 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -14,6 +14,12 @@ #include "rna_internal.h" +const EnumPropertyItem rna_enum_velocity_unit_items[] = { + {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""}, + {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "BLI_math.h" @@ -350,15 +356,9 @@ static void rna_def_cachefile(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_CacheFile_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - static const EnumPropertyItem velocity_unit_items[] = { - {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""}, - {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""}, - {0, NULL, 0, NULL, NULL}, - }; - prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "velocity_unit"); - RNA_def_property_enum_items(prop, velocity_unit_items); + RNA_def_property_enum_items(prop, rna_enum_velocity_unit_items); RNA_def_property_ui_text( prop, "Velocity Unit", diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index dab3cd68d4c..e0ec146a248 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -2644,6 +2644,12 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, gridlines_cell_filter_items); RNA_def_property_ui_text(prop, "Cell Type", "Cell type to be highlighted"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "velocity_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Velocity Scale", "Factor to control the amount of motion blur"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_update"); } static void rna_def_fluid_flow_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 52af96e355d..0e7c332f6a4 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -753,9 +753,9 @@ static void rna_MeshUVLoopLayer_clone_set(PointerRNA *ptr, bool value) /* vertex_color_layers */ -DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_color, ldata, CD_MLOOPCOL) +DEFINE_CUSTOMDATA_LAYER_COLLECTION(vertex_color, ldata, CD_PROP_BYTE_COLOR) DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM( - vertex_color, ldata, CD_MLOOPCOL, active, MeshLoopColorLayer) + vertex_color, ldata, CD_PROP_BYTE_COLOR, active, MeshLoopColorLayer) static void rna_MeshLoopColorLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { @@ -773,22 +773,22 @@ static int rna_MeshLoopColorLayer_data_length(PointerRNA *ptr) static bool rna_MeshLoopColorLayer_active_render_get(PointerRNA *ptr) { - return rna_CustomDataLayer_active_get(ptr, rna_mesh_ldata(ptr), CD_MLOOPCOL, 1); + return rna_CustomDataLayer_active_get(ptr, rna_mesh_ldata(ptr), CD_PROP_BYTE_COLOR, 1); } static bool rna_MeshLoopColorLayer_active_get(PointerRNA *ptr) { - return rna_CustomDataLayer_active_get(ptr, rna_mesh_ldata(ptr), CD_MLOOPCOL, 0); + return rna_CustomDataLayer_active_get(ptr, rna_mesh_ldata(ptr), CD_PROP_BYTE_COLOR, 0); } static void rna_MeshLoopColorLayer_active_render_set(PointerRNA *ptr, bool value) { - rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_MLOOPCOL, 1); + rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_PROP_BYTE_COLOR, 1); } static void rna_MeshLoopColorLayer_active_set(PointerRNA *ptr, bool value) { - rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_MLOOPCOL, 0); + rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_PROP_BYTE_COLOR, 0); } /* sculpt_vertex_color_layers */ @@ -1340,7 +1340,7 @@ static char *rna_MeshLoopColorLayer_path(PointerRNA *ptr) static char *rna_MeshColor_path(PointerRNA *ptr) { - return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_MLOOPCOL); + return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_PROP_BYTE_COLOR); } static char *rna_MeshVertColorLayer_path(PointerRNA *ptr) @@ -1564,7 +1564,7 @@ static PointerRNA rna_Mesh_vertex_color_new(struct Mesh *me, if (index != -1) { ldata = rna_mesh_ldata_helper(me); - cdl = &ldata->layers[CustomData_get_layer_index_n(ldata, CD_MLOOPCOL, index)]; + cdl = &ldata->layers[CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, index)]; } RNA_pointer_create(&me->id, &RNA_MeshLoopColorLayer, cdl, &ptr); @@ -3182,6 +3182,7 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, "rna_Mesh_uv_layer_clone_get", "rna_Mesh_uv_layer_clone_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text( prop, "Clone UV Loop Layer", "UV loop layer to be used as cloning source"); @@ -3197,6 +3198,7 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, "rna_Mesh_uv_layer_stencil_get", "rna_Mesh_uv_layer_stencil_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_ui_text(prop, "Mask UV Loop Layer", "UV loop layer to mask the painted area"); prop = RNA_def_property(srna, "uv_layer_stencil_index", PROP_INT, PROP_UNSIGNED); diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index b1ebe583afc..8447074a3ef 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -159,7 +159,6 @@ static void rna_Mesh_normals_split_custom_set_from_vertices(Mesh *mesh, static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys) { BKE_mesh_transform(mesh, (float(*)[4])mat, shape_keys); - BKE_mesh_normals_tag_dirty(mesh); DEG_id_tag_update(&mesh->id, 0); } diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 16045e6cddf..456f774648a 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1361,13 +1361,13 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_src_itemf( } } } - else if (STREQ(RNA_property_identifier(prop), "layers_vcol_select_vert_src") || - STREQ(RNA_property_identifier(prop), "layers_vcol_select_loop_src")) { + else if (STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_src") || + STREQ(RNA_property_identifier(prop), "layers_vcol_loop_select_src")) { Object *ob_src = dtmd->ob_source; if (ob_src) { AttributeDomain domain = STREQ(RNA_property_identifier(prop), - "layers_vcol_select_vert_src") ? + "layers_vcol_vert_select_src") ? ATTR_DOMAIN_POINT : ATTR_DOMAIN_CORNER; @@ -1389,7 +1389,7 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_src_itemf( cdata = &me_eval->ldata; } - CustomDataType types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; + CustomDataType types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; int idx = 0; for (int i = 0; i < 2; i++) { @@ -1480,12 +1480,16 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_dst_itemf( } else if (STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_dst") || STREQ(RNA_property_identifier(prop), "layers_vcol_loop_select_dst")) { + int multilayer_index = STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_dst") ? + DT_MULTILAYER_INDEX_VCOL_VERT : + DT_MULTILAYER_INDEX_VCOL_LOOP; + /* Only list destination layers if we have a single source! */ - if (dtmd->layers_select_src[DT_MULTILAYER_INDEX_VCOL_LOOP] >= 0) { + if (dtmd->layers_select_src[multilayer_index] >= 0) { Object *ob_dst = CTX_data_active_object(C); /* XXX Is this OK? */ if (ob_dst && ob_dst->data) { - CustomDataType types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; + CustomDataType types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR}; Mesh *me_dst = ob_dst->data; CustomData *cdata = STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_dst") ? diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 31b2d36dcfd..4195782db08 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1420,8 +1420,13 @@ static bNodeSocket *rna_NodeTree_inputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_IN, type, name); - ED_node_tree_propagate_change(NULL, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + if (sock == NULL) { + BKE_report(reports, RPT_ERROR, "Unable to create socket"); + } + else { + ED_node_tree_propagate_change(NULL, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } return sock; } @@ -1435,8 +1440,13 @@ static bNodeSocket *rna_NodeTree_outputs_new( bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_OUT, type, name); - ED_node_tree_propagate_change(NULL, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + if (sock == NULL) { + BKE_report(reports, RPT_ERROR, "Unable to create socket"); + } + else { + ED_node_tree_propagate_change(NULL, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } return sock; } @@ -2134,8 +2144,13 @@ static void rna_GeometryNodeCompare_data_type_update(Main *bmain, Scene *scene, static bool generic_attribute_type_supported(const EnumPropertyItem *item) { - return ELEM( - item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); + return ELEM(item->value, + CD_PROP_FLOAT, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + CD_PROP_BOOL, + CD_PROP_INT32, + CD_PROP_BYTE_COLOR); } static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), @@ -2146,6 +2161,18 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_itemf(bContext return itemf_function_check(rna_enum_attribute_type_items, generic_attribute_type_supported); } +static bool generic_attribute_type_supported_with_socket(const EnumPropertyItem *item) +{ + return generic_attribute_type_supported(item) && !ELEM(item->value, CD_PROP_BYTE_COLOR); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeType_type_with_socket_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, + generic_attribute_type_supported_with_socket); +} + static bool transfer_attribute_type_supported(const EnumPropertyItem *item) { return ELEM( @@ -10209,7 +10236,8 @@ static void def_geo_raycast(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -10261,7 +10289,8 @@ static void def_geo_input_named_attribute(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "The data type used to read the attribute values"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -10275,7 +10304,8 @@ static void def_geo_attribute_capture(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -10502,7 +10532,8 @@ static void def_geo_viewer(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf"); RNA_def_property_enum_default(prop, CD_PROP_FLOAT); RNA_def_property_ui_text(prop, "Data Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); @@ -10532,7 +10563,8 @@ static void def_geo_field_at_index(StructRNA *srna) prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom2"); RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_itemf"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_GeometryNodeAttributeType_type_with_socket_itemf"); RNA_def_property_ui_text(prop, "Data Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 03f800d14d1..6f2264680c8 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -676,7 +676,7 @@ static void rna_ParticleSystem_mcol_on_emitter(ParticleSystem *particlesystem, int vcol_no, float r_mcol[3]) { - if (!CustomData_has_layer(&modifier->mesh_final->ldata, CD_MLOOPCOL)) { + if (!CustomData_has_layer(&modifier->mesh_final->ldata, CD_PROP_BYTE_COLOR)) { BKE_report(reports, RPT_ERROR, "Mesh has no VCol data"); zero_v3(r_mcol); return; diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 2390fdd72f0..a80e9573657 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -1360,7 +1360,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "custom_tx"); RNA_def_property_struct_type(prop, "PoseBone"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Custom Shape Transform", "Bone that defines the display transform of this custom shape"); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 373df6b7444..a7d673d3fe1 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1474,6 +1474,11 @@ static int rna_property_override_diff_propptr(Main *bmain, (is_array ? RNA_property_##_typename##_set_index((_ptr), (_prop), (_index), (_value)) : \ RNA_property_##_typename##_set((_ptr), (_prop), (_value))) +/** + * /return `0` is matching, `-1` if `prop_a < prop_b`, `1` if `prop_a > prop_b`. Note that for + * unquantifiable properties (e.g. pointers or collections), return value should be interpreted as + * a boolean (false == matching, true == not matching). + */ int rna_property_override_diff_default(Main *bmain, PropertyRNAOrID *prop_a, PropertyRNAOrID *prop_b, @@ -1932,25 +1937,25 @@ int rna_property_override_diff_default(Main *bmain, } else if (is_id || is_valid_for_diffing) { if (equals || do_create) { - const int eq = rna_property_override_diff_propptr(bmain, - ptr_a->owner_id, - ptr_b->owner_id, - &iter_a.ptr, - &iter_b.ptr, - mode, - no_ownership, - no_prop_name, - override, - rna_path, - rna_path_len, - PROP_COLLECTION, - propname_a, - propname_b, - idx_a, - idx_b, - flags, - r_override_changed); - equals = equals && eq; + const int comp = rna_property_override_diff_propptr(bmain, + ptr_a->owner_id, + ptr_b->owner_id, + &iter_a.ptr, + &iter_b.ptr, + mode, + no_ownership, + no_prop_name, + override, + rna_path, + rna_path_len, + PROP_COLLECTION, + propname_a, + propname_b, + idx_a, + idx_b, + flags, + r_override_changed); + equals = equals && (comp == 0); } } @@ -2596,10 +2601,11 @@ bool rna_property_override_apply_default(Main *bmain, int item_index_src, item_index_ref; if (RNA_property_collection_lookup_string_index( ptr_src, prop_src, opop->subitem_local_name, &item_ptr_src, &item_index_src) && - RNA_property_collection_lookup_int( - ptr_src, prop_src, item_index_src + 1, &item_ptr_src) && - RNA_property_collection_lookup_string_index( - ptr_dst, prop_dst, opop->subitem_local_name, &item_ptr_ref, &item_index_ref)) { + RNA_property_collection_lookup_string_index(ptr_dst, + prop_dst, + opop->subitem_reference_name, + &item_ptr_ref, + &item_index_ref)) { is_valid = true; item_index_dst = item_index_ref + 1; } @@ -2607,10 +2613,10 @@ bool rna_property_override_apply_default(Main *bmain, if (!is_valid && opop->subitem_local_index >= 0) { /* Find from index. */ if (RNA_property_collection_lookup_int( - ptr_src, prop_src, opop->subitem_local_index + 1, &item_ptr_src) && + ptr_src, prop_src, opop->subitem_local_index, &item_ptr_src) && RNA_property_collection_lookup_int( - ptr_dst, prop_dst, opop->subitem_local_index, &item_ptr_ref)) { - item_index_dst = opop->subitem_local_index + 1; + ptr_dst, prop_dst, opop->subitem_reference_index, &item_ptr_ref)) { + item_index_dst = opop->subitem_reference_index + 1; is_valid = true; } } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 7f0a9627a17..b647a823929 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2630,7 +2630,7 @@ static const EnumPropertyItem *rna_UnitSettings_itemf_wrapper(const int system, EnumPropertyItem adaptive = {0}; adaptive.identifier = "ADAPTIVE"; - adaptive.name = "Adaptive"; + adaptive.name = N_("Adaptive"); adaptive.value = USER_UNIT_ADAPTIVE; RNA_enum_item_add(&items, &totitem, &adaptive); @@ -3092,7 +3092,10 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_object_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "object_flag", SCE_OBJECT_MODE_LOCK); - RNA_def_property_ui_text(prop, "Lock Object Modes", "Restrict select to the current mode"); + RNA_def_property_ui_text(prop, + "Lock Object Modes", + "Restrict selection to objects using the same mode as the active " + "object, to prevent accidental mode switch when selecting"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); static const EnumPropertyItem workspace_tool_items[] = { diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 37c687ddb2b..017e8bde7a1 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -562,8 +562,8 @@ static void rna_PaintModeSettings_canvas_source_update(bContext *C, PointerRNA * { Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - /* When canvas source changes the pbvh would require updates when switching between color - * attributes. */ + /* When canvas source changes the PBVH would require updates when switching between color + * attributes. */ if (ob && ob->type == OB_MESH) { BKE_texpaint_slots_refresh_object(scene, ob); DEG_id_tag_update(&ob->id, 0); @@ -1565,35 +1565,10 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) static void rna_def_curves_sculpt(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *prop; srna = RNA_def_struct(brna, "CurvesSculpt", "Paint"); RNA_def_struct_path_func(srna, "rna_CurvesSculpt_path"); RNA_def_struct_ui_text(srna, "Curves Sculpt Paint", ""); - - prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 6); - RNA_def_property_ui_text( - prop, "Distance", "Radius around curves roots in which no new curves can be added"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - - prop = RNA_def_property(srna, "interpolate_length", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH); - RNA_def_property_ui_text( - prop, "Interpolate Length", "Use length of the curves in close proximity"); - - prop = RNA_def_property(srna, "interpolate_shape", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE); - RNA_def_property_ui_text( - prop, "Interpolate Shape", "Use shape of the curves in close proximity"); - - prop = RNA_def_property(srna, "curve_length", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_range(prop, 0.0, FLT_MAX); - RNA_def_property_ui_text( - prop, - "Curve Length", - "Length of newly added curves when it is not interpolated from other curves"); } void RNA_def_sculpt_paint(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index a27e699ef3d..3fd87a16d28 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2169,11 +2169,12 @@ static void rna_def_channel(BlenderRNA *brna) prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_CHANNEL_LOCK); RNA_def_property_ui_text(prop, "Lock channel", ""); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_CHANNEL_MUTE); RNA_def_property_ui_text(prop, "Mute channel", ""); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_sound_update"); } static void rna_def_editor(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c40a2ed9260..1f68855622f 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -424,7 +424,7 @@ static const EnumPropertyItem rna_enum_shading_color_type_items[] = { {V3D_SHADING_SINGLE_COLOR, "SINGLE", 0, "Single", "Show scene in a single color"}, {V3D_SHADING_OBJECT_COLOR, "OBJECT", 0, "Object", "Show object color"}, {V3D_SHADING_RANDOM_COLOR, "RANDOM", 0, "Random", "Show random object color"}, - {V3D_SHADING_VERTEX_COLOR, "VERTEX", 0, "Color", "Show active color attribute"}, + {V3D_SHADING_VERTEX_COLOR, "VERTEX", 0, "Attribute", "Show active color attribute"}, {V3D_SHADING_TEXTURE_COLOR, "TEXTURE", 0, "Texture", "Show texture"}, {0, NULL, 0, NULL, NULL}, }; @@ -7752,6 +7752,12 @@ static void rna_def_spreadsheet_row_filter(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Color Value", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + prop = RNA_def_property(srna, "value_byte_color", PROP_INT, PROP_NONE); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_text(prop, "Byte Color Value", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + prop = RNA_def_property(srna, "value_string", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Text Value", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL); diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 5b323629a80..12cb35b239d 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -78,6 +78,15 @@ static void rna_Volume_update_is_sequence(Main *bmain, Scene *scene, PointerRNA DEG_relations_tag_update(bmain); } +static void rna_Volume_velocity_grid_set(PointerRNA *ptr, const char *value) +{ + Volume *volume = (Volume *)ptr->data; + if (!BKE_volume_set_velocity_grid_by_name(volume, value)) { + WM_reportf(RPT_ERROR, "Could not find grid with name %s", value); + } + WM_main_add_notifier(NC_GEOM | ND_DATA, volume); +} + /* Grid */ static void rna_VolumeGrid_name_get(PointerRNA *ptr, char *value) @@ -248,6 +257,7 @@ static void rna_def_volume_grid(BlenderRNA *brna) RNA_def_property_string_funcs( prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL); RNA_def_property_ui_text(prop, "Name", "Volume grid name"); + RNA_def_struct_name_property(srna, prop); prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -619,6 +629,55 @@ static void rna_def_volume(BlenderRNA *brna) RNA_def_property_struct_type(prop, "VolumeRender"); RNA_def_property_ui_text(prop, "Render", "Volume render settings for 3D viewport"); + /* Velocity. */ + prop = RNA_def_property(srna, "velocity_grid", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "velocity_grid"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Volume_velocity_grid_set"); + RNA_def_property_ui_text( + prop, + "Velocity Grid", + "Name of the velocity field, or the base name if the velocity is split into multiple grids"); + + prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "velocity_unit"); + RNA_def_property_enum_items(prop, rna_enum_velocity_unit_items); + RNA_def_property_ui_text( + prop, + "Velocity Unit", + "Define how the velocity vectors are interpreted with regard to time, 'frame' means " + "the delta time is 1 frame, 'second' means the delta time is 1 / FPS"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "velocity_scale"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Velocity Scale", "Factor to control the amount of motion blur"); + + /* Scalar grids for velocity. */ + prop = RNA_def_property(srna, "velocity_x_grid", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_x_grid"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Velocity X Grid", + "Name of the grid for the X axis component of the velocity field if it " + "was split into multiple grids"); + + prop = RNA_def_property(srna, "velocity_y_grid", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_y_grid"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Velocity Y Grid", + "Name of the grid for the Y axis component of the velocity field if it " + "was split into multiple grids"); + + prop = RNA_def_property(srna, "velocity_z_grid", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_z_grid"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Velocity Z Grid", + "Name of the grid for the Z axis component of the velocity field if it " + "was split into multiple grids"); + /* Common */ rna_def_animdata_common(srna); } diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 758858c8b1d..c44a4e0b438 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -789,13 +789,6 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, MEM_freeN(full_doubles_map); } - /* In case org dm has dirty normals, or we made some merging, mark normals as dirty in new mesh! - * TODO: we may need to set other dirty flags as well? - */ - if (use_recalc_normals) { - BKE_mesh_normals_tag_dirty(result); - } - if (vgroup_start_cap_remap) { MEM_freeN(vgroup_start_cap_remap); } diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index b6cceade4e2..a7364af10a3 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -229,8 +229,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); - return result; } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 915428f99da..d47f2a130e3 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -501,7 +501,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); } if (result == nullptr) { @@ -536,7 +535,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); } } } diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 61959d242d8..687ff04cedf 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -263,8 +263,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct MEM_freeN(edgeMap); MEM_freeN(faceMap); - BKE_mesh_normals_tag_dirty(result); - /* TODO(sybren): also copy flags & tags? */ return result; } diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 1cb308bb2a0..593610f5bad 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -120,7 +120,7 @@ static void deformVerts(ModifierData *md, uint mvert_num = 0; BKE_mesh_vert_coords_apply(mesh_src, vertexCos); - BKE_mesh_calc_normals(mesh_src); + BKE_mesh_normals_tag_dirty(mesh_src); current_time = DEG_get_ctime(ctx->depsgraph); diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 67515478be5..70f028d6907 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -212,8 +212,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * TIMEIT_END(decim); #endif - BKE_mesh_normals_tag_dirty(result); - return result; } diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index f8cf80b673c..1891ac5df7c 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -91,7 +91,7 @@ static void requiredDataMask(Object *UNUSED(ob), /* mcol */ if (surface->type == MOD_DPAINT_SURFACE_T_PAINT || surface->init_color_type == MOD_DPAINT_INITIAL_VERTEXCOLOR) { - r_cddata_masks->lmask |= CD_MASK_MLOOPCOL; + r_cddata_masks->lmask |= CD_MASK_PROP_BYTE_COLOR; } /* CD_MDEFORMVERT */ if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 14431e5ff2e..49ddcb9a61d 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -102,7 +102,6 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index 87ff648f13d..9e2bb79138e 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1102,7 +1102,6 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* finalization */ BKE_mesh_calc_edges_tessface(explode); BKE_mesh_convert_mfaces_to_mpolys(explode); - BKE_mesh_normals_tag_dirty(explode); if (psmd->psys->lattice_deform_data) { BKE_lattice_deform_data_destroy(psmd->psys->lattice_deform_data); diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index cb26bd1b26b..900dee98268 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -748,7 +748,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) } BKE_mesh_calc_edges_loose(result); - BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 7545bae43b3..85b6cf6a2cd 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -119,6 +119,7 @@ using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; using geo_log::GeometryAttributeInfo; +using geo_log::NamedAttributeUsage; static void initData(ModifierData *md) { @@ -997,30 +998,34 @@ static void store_computed_output_attributes( const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(store.data.type()); const std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data( store.name); - if (meta_data.has_value() && meta_data->domain == store.domain && - meta_data->data_type == data_type) { - /* Copy the data into an existing attribute. */ - blender::bke::WriteAttributeLookup write_attribute = component.attribute_try_get_for_write( - store.name); - if (write_attribute) { - write_attribute.varray.set_all(store.data.data()); - if (write_attribute.tag_modified_fn) { - write_attribute.tag_modified_fn(); - } - } - store.data.type().destruct_n(store.data.data(), store.data.size()); - MEM_freeN(store.data.data()); + + /* Attempt to remove the attribute if it already exists but the domain and type don't match. + * Removing the attribute won't succeed if it is built in and non-removable. */ + if (meta_data.has_value() && + (meta_data->domain != store.domain || meta_data->data_type != data_type)) { + component.attribute_try_delete(store.name); } - else { - /* Replace the existing attribute with the new data. */ - if (meta_data.has_value()) { - component.attribute_try_delete(store.name); - } - component.attribute_try_create(store.name, - store.domain, - blender::bke::cpp_type_to_custom_data_type(store.data.type()), - AttributeInitMove(store.data.data())); + + /* Try to create the attribute reusing the stored buffer. This will only succeed if the + * attribute didn't exist before, or if it existed but was removed above. */ + if (component.attribute_try_create( + store.name, + store.domain, + blender::bke::cpp_type_to_custom_data_type(store.data.type()), + AttributeInitMove(store.data.data()))) { + continue; + } + + OutputAttribute attribute = component.attribute_try_get_for_output_only( + store.name, store.domain, data_type); + if (attribute) { + attribute.varray().set_all(store.data.data()); + attribute.save(); } + + /* We were unable to reuse the data, so it must be destructed and freed. */ + store.data.type().destruct_n(store.data.data(), store.data.size()); + MEM_freeN(store.data.data()); } } @@ -1619,6 +1624,71 @@ static void output_attribute_panel_draw(const bContext *C, Panel *panel) } } +static void used_attributes_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data); + + if (nmd->runtime_eval_log == nullptr) { + return; + } + const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log); + Map<std::string, NamedAttributeUsage> usage_by_attribute; + log.foreach_node_log([&](const geo_log::NodeLog &node_log) { + for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) { + usage_by_attribute.lookup_or_add_as(used_attribute.name, + used_attribute.usage) |= used_attribute.usage; + } + }); + + if (usage_by_attribute.is_empty()) { + uiItemL(layout, IFACE_("No named attributes used"), ICON_INFO); + return; + } + + Vector<std::pair<StringRefNull, NamedAttributeUsage>> sorted_used_attribute; + for (auto &&item : usage_by_attribute.items()) { + sorted_used_attribute.append({item.key, item.value}); + } + std::sort(sorted_used_attribute.begin(), sorted_used_attribute.end()); + + for (const auto &pair : sorted_used_attribute) { + const StringRefNull attribute_name = pair.first; + const NamedAttributeUsage usage = pair.second; + + /* #uiLayoutRowWithHeading doesn't seem to work in this case. */ + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + + std::stringstream ss; + Vector<std::string> usages; + if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) { + usages.append(TIP_("Read")); + } + if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) { + usages.append(TIP_("Write")); + } + if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) { + usages.append(TIP_("Remove")); + } + for (const int i : usages.index_range()) { + ss << usages[i]; + if (i < usages.size() - 1) { + ss << ", "; + } + } + + uiLayout *row = uiLayoutRow(split, false); + uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); + uiLayoutSetActive(row, false); + uiItemL(row, ss.str().c_str(), ICON_NONE); + + row = uiLayoutRow(split, false); + uiItemL(row, attribute_name.c_str(), ICON_NONE); + } +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); @@ -1628,6 +1698,12 @@ static void panelRegister(ARegionType *region_type) nullptr, output_attribute_panel_draw, panel_type); + modifier_subpanel_register(region_type, + "used_attributes", + N_("Used Attributes"), + nullptr, + used_attributes_panel_draw, + panel_type); } static void blendWrite(BlendWriter *writer, const ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 68207d84015..e43d2b4ad85 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1635,10 +1635,8 @@ class GeometryNodesEvaluator { if (conversions_.is_convertible(from_base_type, to_base_type)) { if (from_field_type->is_field(from_value)) { const GField &from_field = *from_field_type->get_field_ptr(from_value); - const MultiFunction &fn = *conversions_.get_conversion_multi_function( - MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); - auto operation = std::make_shared<fn::FieldOperation>(fn, Vector<GField>{from_field}); - to_field_type->construct_from_field(to_value, GField(std::move(operation), 0)); + to_field_type->construct_from_field(to_value, + conversions_.try_convert(from_field, to_base_type)); } else { to_field_type->default_construct(to_value); diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 6ded702ceda..b5411eee883 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -302,8 +302,6 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co } } - BKE_mesh_normals_tag_dirty(result); - return result; } @@ -361,7 +359,6 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes if (omd->geometry_mode == MOD_OCEAN_GEOM_GENERATE) { result = generate_ocean_geometry(omd, mesh, resolution); - BKE_mesh_normals_tag_dirty(result); } else if (omd->geometry_mode == MOD_OCEAN_GEOM_DISPLACE) { result = (Mesh *)BKE_id_copy_ex(NULL, &mesh->id, NULL, LIB_ID_COPY_LOCALIZE); @@ -376,17 +373,17 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes /* add vcols before displacement - allows lookup based on position */ if (omd->flag & MOD_OCEAN_GENERATE_FOAM) { - if (CustomData_number_of_layers(&result->ldata, CD_MLOOPCOL) < MAX_MCOL) { + if (CustomData_number_of_layers(&result->ldata, CD_PROP_BYTE_COLOR) < MAX_MCOL) { const int polys_num = result->totpoly; const int loops_num = result->totloop; MLoop *mloops = result->mloop; MLoopCol *mloopcols = CustomData_add_layer_named( - &result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, loops_num, omd->foamlayername); + &result->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, NULL, loops_num, omd->foamlayername); MLoopCol *mloopcols_spray = NULL; if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) { mloopcols_spray = CustomData_add_layer_named( - &result->ldata, CD_MLOOPCOL, CD_CALLOC, NULL, loops_num, omd->spraylayername); + &result->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, NULL, loops_num, omd->spraylayername); } if (mloopcols) { /* unlikely to fail */ @@ -472,6 +469,8 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes } } + BKE_mesh_normals_tag_dirty(mesh); + if (allocated_ocean) { BKE_ocean_free(omd->ocean); omd->ocean = NULL; @@ -490,15 +489,7 @@ static Mesh *doOcean(ModifierData *UNUSED(md), const ModifierEvalContext *UNUSED static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { - Mesh *result; - - result = doOcean(md, ctx, mesh); - - if (result != mesh) { - BKE_mesh_normals_tag_dirty(result); - } - - return result; + return doOcean(md, ctx, mesh); } // #define WITH_OCEANSIM static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 205a1986552..2a8d2454670 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -59,7 +59,7 @@ static void requiredDataMask(Object *UNUSED(ob), ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md; if (pimd->index_layer_name[0] != '\0' || pimd->value_layer_name[0] != '\0') { - r_cddata_masks->lmask |= CD_MASK_MLOOPCOL; + r_cddata_masks->lmask |= CD_MASK_PROP_BYTE_COLOR; } } @@ -328,9 +328,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * orig_mloop = mesh->mloop; MLoopCol *mloopcols_index = CustomData_get_layer_named( - &result->ldata, CD_MLOOPCOL, pimd->index_layer_name); + &result->ldata, CD_PROP_BYTE_COLOR, pimd->index_layer_name); MLoopCol *mloopcols_value = CustomData_get_layer_named( - &result->ldata, CD_MLOOPCOL, pimd->value_layer_name); + &result->ldata, CD_PROP_BYTE_COLOR, pimd->value_layer_name); int *vert_part_index = NULL; float *vert_part_value = NULL; if (mloopcols_index != NULL) { @@ -530,8 +530,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_SAFE_FREE(vert_part_index); MEM_SAFE_FREE(vert_part_value); - BKE_mesh_normals_tag_dirty(result); - return result; } diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 7df7ba7c1db..032227307e7 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -158,7 +158,6 @@ static void deformVerts(ModifierData *md, /* make new mesh */ psmd->mesh_final = BKE_mesh_copy_for_eval(mesh_src, false); BKE_mesh_vert_coords_apply(psmd->mesh_final, vertexCos); - BKE_mesh_calc_normals(psmd->mesh_final); BKE_mesh_tessface_ensure(psmd->mesh_final); diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index f6f4746f44f..288faee247f 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -206,7 +206,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); - BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 4ace6404388..05072cf340c 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -384,7 +384,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * mvert_new = result->mvert; float(*vert_normals_new)[3] = BKE_mesh_vertex_normals_for_write(result); - BKE_mesh_vertex_normals_clear_dirty(result); mpoly_new = result->mpoly; mloop_new = result->mloop; medge_new = result->medge; @@ -1120,7 +1119,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } if ((ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f)) { - Mesh *result_prev = result; result = mesh_remove_doubles_on_axis(result, mvert_new, totvert, @@ -1128,13 +1126,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * axis_vec, ob_axis != NULL ? mtx_tx[3] : NULL, ltmd->merge_dist); - if (result != result_prev) { - BKE_mesh_normals_tag_dirty(result); - } } - if ((ltmd->flag & MOD_SCREW_NORMAL_CALC) == 0) { - BKE_mesh_normals_tag_dirty(result); + if ((ltmd->flag & MOD_SCREW_NORMAL_CALC)) { + BKE_mesh_vertex_normals_clear_dirty(result); } return result; diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index b950578ff1b..7f96dcb82fb 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1950,8 +1950,6 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, origmesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); - skin_set_orig_indices(result); return result; diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 8a84cd0a3bf..8a5b600974c 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -2016,8 +2016,6 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } - BKE_mesh_normals_tag_dirty(result); - /* Make edges. */ { uint i = 0; diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 3e75e325e44..c2869e590bd 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -129,7 +129,7 @@ static void deformVerts(ModifierData *md, MVert *x, *v; BKE_mesh_vert_coords_apply(surmd->mesh, vertexCos); - BKE_mesh_calc_normals(surmd->mesh); + BKE_mesh_normals_tag_dirty(surmd->mesh); mesh_verts_num = surmd->mesh->totvert; diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index d7e57c1f6e5..f1e8ef5bf38 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -89,8 +89,6 @@ static Mesh *triangulate_mesh(Mesh *mesh, me->flag |= ME_EDGEDRAW | ME_EDGERENDER; } - BKE_mesh_normals_tag_dirty(result); - return result; } diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index dae7d19844e..b657ea87244 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -97,8 +97,6 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); - return result; } 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 87f000897ed..877dc05211d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -131,8 +131,6 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } plConvexHullDelete(hull); - - BKE_mesh_normals_tag_dirty(result); return result; } 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 c3b1a141f4a..8a0c900fbde 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -1225,8 +1225,6 @@ static void do_mesh_separation(GeometrySet &geometry_set, } BKE_mesh_calc_edges_loose(mesh_out); - /* Tag to recalculate normals later. */ - BKE_mesh_normals_tag_dirty(mesh_out); geometry_set.replace_mesh(mesh_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index 2894e608819..dcd9bcfb034 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -897,7 +897,6 @@ static void calc_dual_mesh(GeometrySet &geometry_set, copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]); } memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size()); - BKE_mesh_normals_tag_dirty(mesh_out); geometry_set.replace_mesh(mesh_out); } 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 c03a340a0c8..84acab47661 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -41,8 +41,6 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); - return result; } 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 e53be5213f6..636ecb8ab41 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 @@ -407,7 +407,6 @@ Mesh *create_cuboid_mesh(const float3 size, calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop}); BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); calculate_uvs(config, mesh); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index e1f85be0f80..3c0ccb01673 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -50,7 +50,6 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_normals_tag_dirty(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); mesh_component.replace(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 296b40c6c49..507c6e81b1f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -18,8 +18,10 @@ namespace blender::nodes::node_geo_set_material_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Geometry")) - .supported_type( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_POINT_CLOUD}); + .supported_type({GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_VOLUME, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); b.add_input<decl::Material>(N_("Material")).hide_label(); b.add_output<decl::Geometry>(N_("Geometry")); 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 92614d1a31d..b51fc063ab8 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 @@ -5,6 +5,8 @@ #include "NOD_socket_search_link.hh" +#include "BKE_type_conversions.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_store_named_attribute_cc { @@ -55,7 +57,8 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability( + ntree, socket_color4f, ELEM(data_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)); nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); } @@ -149,6 +152,12 @@ static void node_geo_exec(GeoNodeExecParams params) case CD_PROP_COLOR: field = params.get_input<Field<ColorGeometry4f>>("Value_Color"); break; + case CD_PROP_BYTE_COLOR: { + field = params.get_input<Field<ColorGeometry4f>>("Value_Color"); + field = bke::get_implicit_type_conversions().try_convert(field, + CPPType::get<ColorGeometry4b>()); + break; + } case CD_PROP_BOOL: field = params.get_input<Field<bool>>("Value_Bool"); break; 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 a5fe491e12b..4832feac5bd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -119,7 +119,6 @@ static void node_geo_exec(GeoNodeExecParams params) } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_normals_tag_dirty(mesh_out); mesh_component.replace(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index cc115ee3b3f..e95db205920 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -43,7 +43,6 @@ static void translate_mesh(Mesh &mesh, const float3 translation) static void transform_mesh(Mesh &mesh, const float4x4 &transform) { BKE_mesh_transform(&mesh, transform.values, false); - BKE_mesh_normals_tag_dirty(&mesh); } static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 76cdbfb140f..992470e8279 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -62,7 +62,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, nullptr, nullptr, nullptr); Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh); BM_mesh_free(bm); - BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index c30c05bdd0d..e5827c24320 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -139,7 +139,6 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index d917106807c..c5f40a46ca3 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -722,7 +722,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node } case SH_NODE_ADD_SHADER: { /* Simple passthrough node. Each original inputs will get the same weight. */ - /* TODO(fclem) Better use some kind of reroute node? */ + /* TODO(fclem): Better use some kind of reroute node? */ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_ADD; nodes_copy[id]->tmp_flag = -2; /* Copy */ @@ -761,14 +761,14 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node } id++; /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */ - /* TODO(fclem) Better use some kind of reroute node? */ + /* TODO(fclem): Better use some kind of reroute node? */ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_ADD; nodes_copy[id]->tmp_flag = -2; /* Copy */ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) ->value = 0.0f; id++; - /* Link between nodes for the substraction. */ + /* Link between nodes for the subtraction. */ fromnode = nodes_copy[id_start]; tonode = nodes_copy[id_start + 1]; fromsock = ntree_shader_node_output_get(fromnode, 0); @@ -890,8 +890,8 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node break; } - /* Manually add the link to the socket to avoid calling - * BKE_ntree_update_main_tree(G.main, oop, nullptr. */ + /* Manually add the link to the socket to avoid calling: + * `BKE_ntree_update_main_tree(G.main, oop, nullptr)`. */ fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); BLI_assert(fromsock->link); } @@ -957,8 +957,8 @@ static void ntree_shader_shader_to_rgba_branch(bNodeTree *ntree, bNode *output_n LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->tmp_flag = -1; } - /* First gather the shader_to_rgba nodes linked to the ouput. This is separate to avoid - * conflicting usage of the node->tmp_flag. */ + /* First gather the shader_to_rgba nodes linked to the output. This is separate to avoid + * conflicting usage of the `node->tmp_flag`. */ Vector<bNode *> shader_to_rgba_nodes; nodeChainIterBackwards(ntree, output_node, shader_to_rgba_node_gather, &shader_to_rgba_nodes, 0); @@ -971,7 +971,7 @@ static void ntree_shader_shader_to_rgba_branch(bNodeTree *ntree, bNode *output_n bNode *start_node_copy = ntree_shader_copy_branch( ntree, start_node, closure_node_filter, nullptr, 0); /* Replace node copy link. This assumes that every node possibly connected to the closure input - * has only one ouput. */ + * has only one output. */ bNodeSocket *closure_output = ntree_shader_node_output_get(start_node_copy, 0); nodeRemLink(ntree, closure_input->link); nodeAddLink(ntree, start_node_copy, closure_output, shader_to_rgba, closure_input); diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc index bb01818e734..d01271c15d3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc +++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc @@ -37,24 +37,6 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage); bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY; - if (GPU_material_is_volume_shader(mat) && is_varying) { - if (out[0].hasoutput) { - out[0].link = GPU_volume_grid(mat, attr->name, GPU_VOLUME_DEFAULT_0); - } - if (out[1].hasoutput) { - out[1].link = GPU_volume_grid(mat, attr->name, GPU_VOLUME_DEFAULT_0); - } - if (out[2].hasoutput) { - out[2].link = GPU_volume_grid(mat, attr->name, GPU_VOLUME_DEFAULT_0); - } - if (out[3].hasoutput) { - static const float default_alpha = 1.0f; - out[3].link = GPU_constant(&default_alpha); - } - - return 1; - } - GPUNodeLink *cd_attr; if (is_varying) { @@ -64,6 +46,13 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER); } + if (STREQ(attr->name, "color")) { + GPU_link(mat, "node_attribute_color", cd_attr, &cd_attr); + } + else if (STREQ(attr->name, "temperature")) { + GPU_link(mat, "node_attribute_temperature", cd_attr, &cd_attr); + } + GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr); int i; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc index dbff092234b..df5fbac3ab5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc @@ -37,15 +37,15 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; ImageUser *iuser = &tex_original->iuser; eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; - /* TODO(fclem): For now assume mipmap is always enabled. */ + /* TODO(@fclem): For now assume mipmap is always enabled. */ if (true) { sampler |= GPU_SAMPLER_MIPMAP; } GPUNodeLink *outalpha; - /* HACK(fclem): For lookdev mode: do not compile an empty environment and just create an empty - * texture entry point. We manually bind to it after DRW_shgroup_add_material_resources(). */ + /* HACK(@fclem): For lookdev mode: do not compile an empty environment and just create an empty + * texture entry point. We manually bind to it after #DRW_shgroup_add_material_resources(). */ if (!ima && !GPU_material_flag_get(mat, GPU_MATFLAG_LOOKDEV_HACK)) { return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc index 6676a7653fe..a202312f8d8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc @@ -20,16 +20,18 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat, GPUNodeStack *out) { if (out[0].hasoutput) { - out[0].link = GPU_volume_grid(mat, "color", GPU_VOLUME_DEFAULT_0); + out[0].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "color"); + GPU_link(mat, "node_attribute_color", out[0].link, &out[0].link); } if (out[1].hasoutput) { - out[1].link = GPU_volume_grid(mat, "density", GPU_VOLUME_DEFAULT_0); + out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density"); } if (out[2].hasoutput) { - out[2].link = GPU_volume_grid(mat, "flame", GPU_VOLUME_DEFAULT_0); + out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame"); } if (out[3].hasoutput) { - out[3].link = GPU_volume_grid(mat, "temperature", GPU_VOLUME_DEFAULT_0); + out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature"); + GPU_link(mat, "node_attribute_temperature", out[3].link, &out[3].link); } return true; diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc index d414b4b2ef7..07e700e550a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc @@ -43,6 +43,18 @@ static void node_shader_init_volume_principled(bNodeTree *UNUSED(ntree), bNode * } } +static void attribute_post_process(GPUMaterial *mat, + const char *attribute_name, + GPUNodeLink **attribute_link) +{ + if (STREQ(attribute_name, "color")) { + GPU_link(mat, "node_attribute_color", *attribute_link, attribute_link); + } + else if (STREQ(attribute_name, "temperature")) { + GPU_link(mat, "node_attribute_temperature", *attribute_link, attribute_link); + } +} + static int node_shader_gpu_volume_principled(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -67,28 +79,29 @@ static int node_shader_gpu_volume_principled(GPUMaterial *mat, } if (STREQ(sock->name, "Density Attribute")) { - density = GPU_volume_grid(mat, attribute_name, GPU_VOLUME_DEFAULT_1); + density = GPU_attribute_with_default(mat, CD_AUTO_FROM_NAME, attribute_name, GPU_DEFAULT_1); + attribute_post_process(mat, attribute_name, &density); } else if (STREQ(sock->name, "Color Attribute")) { - color = GPU_volume_grid(mat, attribute_name, GPU_VOLUME_DEFAULT_1); + color = GPU_attribute_with_default(mat, CD_AUTO_FROM_NAME, attribute_name, GPU_DEFAULT_1); + attribute_post_process(mat, attribute_name, &color); } else if (use_blackbody && STREQ(sock->name, "Temperature Attribute")) { - temperature = GPU_volume_grid(mat, attribute_name, GPU_VOLUME_DEFAULT_0); + temperature = GPU_attribute(mat, CD_AUTO_FROM_NAME, attribute_name); + attribute_post_process(mat, attribute_name, &temperature); } } /* Default values if attributes not found. */ + static float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; if (!density) { - static float one = 1.0f; - density = GPU_constant(&one); + density = GPU_constant(white); } if (!color) { - static float white[4] = {1.0f, 1.0f, 1.0f, 1.0f}; color = GPU_constant(white); } if (!temperature) { - static float one = 1.0f; - temperature = GPU_constant(&one); + temperature = GPU_constant(white); } /* Create blackbody spectrum. */ diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index e591dfa2929..58bfb922327 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -193,7 +193,7 @@ static PyGetSetDef bpy_bmlayeraccess_vert_getseters[] = { (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, bpy_bmlayeraccess_collection__color_doc, - (void *)CD_MLOOPCOL}, + (void *)CD_PROP_BYTE_COLOR}, {"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, @@ -254,7 +254,7 @@ static PyGetSetDef bpy_bmlayeraccess_edge_getseters[] = { (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, bpy_bmlayeraccess_collection__color_doc, - (void *)CD_MLOOPCOL}, + (void *)CD_PROP_BYTE_COLOR}, {"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, @@ -307,7 +307,7 @@ static PyGetSetDef bpy_bmlayeraccess_face_getseters[] = { (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, bpy_bmlayeraccess_collection__color_doc, - (void *)CD_MLOOPCOL}, + (void *)CD_PROP_BYTE_COLOR}, {"string", (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, @@ -365,7 +365,7 @@ static PyGetSetDef bpy_bmlayeraccess_loop_getseters[] = { (getter)bpy_bmlayeraccess_collection_get, (setter)NULL, bpy_bmlayeraccess_collection__color_doc, - (void *)CD_MLOOPCOL}, + (void *)CD_PROP_BYTE_COLOR}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -1134,7 +1134,7 @@ PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer) ret = BPy_BMLoopUV_CreatePyObject(value); break; } - case CD_MLOOPCOL: { + case CD_PROP_BYTE_COLOR: { ret = BPy_BMLoopColor_CreatePyObject(value); break; } @@ -1236,7 +1236,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj ret = BPy_BMLoopUV_AssignPyObject(value, py_value); break; } - case CD_MLOOPCOL: { + case CD_PROP_BYTE_COLOR: { ret = BPy_BMLoopColor_AssignPyObject(value, py_value); break; } diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 232d4775746..02bcf80aa5d 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -82,6 +82,18 @@ static PyObject *pygpu_batch__tp_new(PyTypeObject *UNUSED(type), PyObject *args, } BLI_assert(prim_type.value_found != GPU_PRIM_NONE); + if (prim_type.value_found == GPU_PRIM_LINE_LOOP) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "'LINE_LOOP' is deprecated. Please use 'LINE_STRIP' and close the segment.", + 1); + } + else if (prim_type.value_found == GPU_PRIM_TRI_FAN) { + PyErr_WarnEx( + PyExc_DeprecationWarning, + "'TRI_FAN' is deprecated. Please use 'TRI_STRIP' or 'TRIS' and try modifying your " + "vertices or indices to match the topology.", + 1); + } if (py_vertbuf == NULL) { PyErr_Format(PyExc_TypeError, exc_str_missing_arg, _keywords[1], 2); diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 9fe4bdcbaa0..77333c6dfea 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -30,17 +30,37 @@ /** \name Enum Conversion. * \{ */ -#define PYDOC_BUILTIN_SHADER_LIST \ - " - ``2D_FLAT_COLOR``\n" \ - " - ``2D_IMAGE``\n" \ - " - ``2D_SMOOTH_COLOR``\n" \ - " - ``2D_UNIFORM_COLOR``\n" \ - " - ``3D_FLAT_COLOR``\n" \ - " - ``3D_SMOOTH_COLOR``\n" \ - " - ``3D_UNIFORM_COLOR``\n" \ - " - ``3D_POLYLINE_FLAT_COLOR``\n" \ - " - ``3D_POLYLINE_SMOOTH_COLOR``\n" \ - " - ``3D_POLYLINE_UNIFORM_COLOR``\n" +#define PYDOC_BUILTIN_SHADER_DESCRIPTION \ + "``2D_FLAT_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: none\n" \ + "``2D_IMAGE``\n" \ + " :Attributes: vec3 pos, vec2 texCoord\n" \ + " :Uniforms: sampler2D image\n" \ + "``2D_SMOOTH_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: none\n" \ + "``2D_UNIFORM_COLOR``\n" \ + " :Attributes: vec3 pos\n" \ + " :Uniforms: vec4 color\n" \ + "``3D_FLAT_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: none\n" \ + "``3D_SMOOTH_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: none\n" \ + "``3D_UNIFORM_COLOR``\n" \ + " :Attributes: vec3 pos\n" \ + " :Uniforms: vec4 color\n" \ + "``3D_POLYLINE_FLAT_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: vec2 viewportSize, float lineWidth\n" \ + "``3D_POLYLINE_SMOOTH_COLOR``\n" \ + " :Attributes: vec3 pos, vec4 color\n" \ + " :Uniforms: vec2 viewportSize, float lineWidth\n" \ + "``3D_POLYLINE_UNIFORM_COLOR``\n" \ + " :Attributes: vec3 pos\n" \ + " :Uniforms: vec2 viewportSize, float lineWidth\n" static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"}, @@ -731,7 +751,9 @@ static PyObject *pygpu_shader_unbind(BPyGPUShader *UNUSED(self)) 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" + " 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" @@ -739,11 +761,7 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc, "``CLIPPED`` value to the config parameter. Note that in this case you also need to " "manually set the value of ``mat4 ModelMatrix``.\n" "\n" - " For more details, you can check the shader code with the\n" - " :func:`gpu.shader.code_from_builtin` function.\n" - "\n" - " :param shader_name: One of these builtin shader names:\n" - "\n" PYDOC_BUILTIN_SHADER_LIST + " :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" @@ -784,52 +802,6 @@ static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg return BPyGPUShader_CreatePyObject(shader, true); } -PyDoc_STRVAR(pygpu_shader_code_from_builtin_doc, - ".. function:: code_from_builtin(pygpu_shader_name)\n" - "\n" - " Exposes the internal shader code for consultation.\n" - "\n" - " :param pygpu_shader_name: One of these builtin shader names:\n" - "\n" PYDOC_BUILTIN_SHADER_LIST - " :type pygpu_shader_name: str\n" - " :return: Vertex, fragment and geometry shader codes.\n" - " :rtype: dict\n"); -static PyObject *pygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyObject *arg) -{ - const char *vert; - const char *frag; - const char *geom; - const char *defines; - - PyObject *item, *r_dict; - - struct PyC_StringEnum pygpu_bultinshader = {pygpu_shader_builtin_items}; - if (!PyC_ParseStringEnum(arg, &pygpu_bultinshader)) { - return NULL; - } - - GPU_shader_get_builtin_shader_code( - pygpu_bultinshader.value_found, &vert, &frag, &geom, &defines); - - r_dict = PyDict_New(); - - PyDict_SetItemString(r_dict, "vertex_shader", item = PyUnicode_FromString(vert)); - Py_DECREF(item); - - PyDict_SetItemString(r_dict, "fragment_shader", item = PyUnicode_FromString(frag)); - Py_DECREF(item); - - if (geom) { - PyDict_SetItemString(r_dict, "geometry_shader", item = PyUnicode_FromString(geom)); - Py_DECREF(item); - } - if (defines) { - PyDict_SetItemString(r_dict, "defines", item = PyUnicode_FromString(defines)); - Py_DECREF(item); - } - return r_dict; -} - PyDoc_STRVAR(pygpu_shader_create_from_info_doc, ".. function:: create_from_info(shader_info)\n" "\n" @@ -868,10 +840,6 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = { (PyCFunction)pygpu_shader_from_builtin, METH_VARARGS | METH_KEYWORDS, pygpu_shader_from_builtin_doc}, - {"code_from_builtin", - (PyCFunction)pygpu_shader_code_from_builtin, - METH_O, - pygpu_shader_code_from_builtin_doc}, {"create_from_info", (PyCFunction)pygpu_shader_create_from_info, METH_O, @@ -887,28 +855,7 @@ PyDoc_STRVAR(pygpu_shader_module__tp_doc, "All built-in shaders have the ``mat4 ModelViewProjectionMatrix`` uniform.\n" "\n" "Its value must be modified using the :class:`gpu.matrix` module.\n" - "\n" - "``2D_UNIFORM_COLOR``\n" - " :Attributes: vec3 pos\n" - " :Uniforms: vec4 color\n" - "``2D_FLAT_COLOR``\n" - " :Attributes: vec3 pos, vec4 color\n" - " :Uniforms: none\n" - "``2D_SMOOTH_COLOR``\n" - " :Attributes: vec3 pos, vec4 color\n" - " :Uniforms: none\n" - "``2D_IMAGE``\n" - " :Attributes: vec3 pos, vec2 texCoord\n" - " :Uniforms: sampler2D image\n" - "``3D_UNIFORM_COLOR``\n" - " :Attributes: vec3 pos\n" - " :Uniforms: vec4 color\n" - "``3D_FLAT_COLOR``\n" - " :Attributes: vec3 pos, vec4 color\n" - " :Uniforms: none\n" - "``3D_SMOOTH_COLOR``\n" - " :Attributes: vec3 pos, vec4 color\n" - " :Uniforms: none\n"); + "\n" PYDOC_BUILTIN_SHADER_DESCRIPTION); static PyModuleDef pygpu_shader_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.shader", diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 86dc5800b67..e4e198ab812 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC bpy_rna_anim.c bpy_rna_array.c bpy_rna_callback.c + bpy_rna_context.c bpy_rna_data.c bpy_rna_driver.c bpy_rna_gizmo.c @@ -101,6 +102,7 @@ set(SRC bpy_rna.h bpy_rna_anim.h bpy_rna_callback.h + bpy_rna_context.h bpy_rna_data.h bpy_rna_driver.h bpy_rna_gizmo.h diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index db0067fc18e..95879b02295 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -60,6 +60,23 @@ static wmOperatorType *ot_lookup_from_py_string(PyObject *value, const char *py_ return ot; } +static void op_context_override_deprecated_warning(const char *action, const char *opname) +{ + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + /* Use stack level 2 as this call is wrapped by `release/scripts/modules/bpy/ops.py`, + * An extra stack level is needed to show the warning in the authors script. */ + 2, + "Passing in context overrides is deprecated in favor of " + "Context.temp_override(..), %s \"%s\"", + action, + opname) < 0) { + /* The function has no return value, the exception cannot + * be reported to the caller, so just log it. */ + PyErr_WriteUnraisable(NULL); + } +} + static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) { wmOperatorType *ot; @@ -113,7 +130,10 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) if (ELEM(context_dict, NULL, Py_None)) { context_dict = NULL; } - else if (!PyDict_Check(context_dict)) { + else if (PyDict_Check(context_dict)) { + op_context_override_deprecated_warning("polling", opname); + } + else { PyErr_Format(PyExc_TypeError, "Calling operator \"bpy.ops.%s.poll\" error, " "custom context expected a dict or None, got a %.200s", @@ -218,7 +238,10 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) if (ELEM(context_dict, NULL, Py_None)) { context_dict = NULL; } - else if (!PyDict_Check(context_dict)) { + else if (PyDict_Check(context_dict)) { + op_context_override_deprecated_warning("calling", opname); + } + else { PyErr_Format(PyExc_TypeError, "Calling operator \"bpy.ops.%s\" error, " "custom context expected a dict or None, got a %.200s", diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 5db66405403..c67f0c028cf 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -4398,7 +4398,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb } } - /* Fallback to standard py, delattr/setattr. */ + /* Fallback to standard Python's `delattr/setattr`. */ return PyType_Type.tp_setattro(cls, attr, value); } @@ -7872,6 +7872,50 @@ StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *e return srna; } +const PointerRNA *pyrna_struct_as_ptr(PyObject *py_obj, const StructRNA *srna) +{ + BPy_StructRNA *bpy_srna = (BPy_StructRNA *)py_obj; + if (!BPy_StructRNA_Check(py_obj) || !RNA_struct_is_a(bpy_srna->ptr.type, srna)) { + PyErr_Format(PyExc_TypeError, + "Expected a \"bpy.types.%.200s\" not a \"%.200s\"", + RNA_struct_identifier(srna), + Py_TYPE(py_obj)->tp_name); + return NULL; + } + PYRNA_STRUCT_CHECK_OBJ(bpy_srna); + return &bpy_srna->ptr; +} + +const PointerRNA *pyrna_struct_as_ptr_or_null(PyObject *py_obj, const StructRNA *srna) +{ + if (py_obj == Py_None) { + return &PointerRNA_NULL; + } + return pyrna_struct_as_ptr(py_obj, srna); +} + +int pyrna_struct_as_ptr_parse(PyObject *o, void *p) +{ + struct BPy_StructRNA_Parse *srna_parse = p; + BLI_assert(srna_parse->type != NULL); + srna_parse->ptr = pyrna_struct_as_ptr(o, srna_parse->type); + if (srna_parse->ptr == NULL) { + return 0; + } + return 1; +} + +int pyrna_struct_as_ptr_or_null_parse(PyObject *o, void *p) +{ + struct BPy_StructRNA_Parse *srna_parse = p; + BLI_assert(srna_parse->type != NULL); + srna_parse->ptr = pyrna_struct_as_ptr_or_null(o, srna_parse->type); + if (srna_parse->ptr == NULL) { + return 0; + } + return 1; +} + /* Orphan functions, not sure where they should go. */ StructRNA *srna_from_self(PyObject *self, const char *error_prefix) { diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 573daac6e95..91fa0ea2c8d 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -187,6 +187,33 @@ PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop); int pyrna_deferred_register_class(struct StructRNA *srna, PyTypeObject *py_class); +const PointerRNA *pyrna_struct_as_ptr(PyObject *py_obj, const StructRNA *srna); +const PointerRNA *pyrna_struct_as_ptr_or_null(PyObject *py_obj, const StructRNA *srna); + +/** + * Struct used for RNA argument parsing functions: + * - #pyrna_struct_as_ptr_parse + * - #pyrna_struct_as_ptr_or_null_parse + */ +struct BPy_StructRNA_Parse { + /** The struct RNA must match this type. */ + StructRNA *type; + /** Result, may be `PointerRNA_NULL` if #pyrna_struct_as_ptr_or_null_parse is used. */ + const PointerRNA *ptr; +}; + +/** + * Sets #BPy_StructRNA_Parse.ptr to the value in the #BPy_StructRNA.ptr (from `o`) + * or raise an error if the type isn't a #BPy_StructRNA. + * + * Use with #PyArg_ParseTuple's `O&` formatting. + */ +int pyrna_struct_as_ptr_parse(PyObject *o, void *p); +/** + * A version of #pyrna_struct_as_ptr_parse that maps Python's `None` to #PointerRNA_NULL. + */ +int pyrna_struct_as_ptr_or_null_parse(PyObject *o, void *p); + void pyrna_struct_type_extend_capi(struct StructRNA *srna, struct PyMethodDef *py_method, struct PyGetSetDef *py_getset); diff --git a/source/blender/python/intern/bpy_rna_context.c b/source/blender/python/intern/bpy_rna_context.c new file mode 100644 index 00000000000..811552ce938 --- /dev/null +++ b/source/blender/python/intern/bpy_rna_context.c @@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup pythonintern + * + * This file adds some helper methods to the context, that cannot fit well in RNA itself. + */ + +#include <Python.h> + +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "bpy_rna_context.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "bpy_rna.h" + +/* -------------------------------------------------------------------- */ +/** \name Temporary Context Override (Python Context Manager) + * \{ */ + +typedef struct ContextStore { + wmWindow *win; + bool win_is_set; + ScrArea *area; + bool area_is_set; + ARegion *region; + bool region_is_set; +} ContextStore; + +typedef struct BPyContextTempOverride { + PyObject_HEAD /* Required Python macro. */ + bContext *context; + + ContextStore ctx_init; + ContextStore ctx_temp; + /** Bypass Python overrides set when calling an operator from Python. */ + struct bContext_PyState py_state; + /** + * This dictionary is used to store members that don't have special handling, + * see: #bpy_context_temp_override_extract_known_args, + * these will then be accessed via #BPY_context_member_get. + * + * This also supports nested *stacking*, so a nested temp-context-overrides + * will overlay the new members on the old members (instead of ignoring them). + */ + PyObject *py_state_context_dict; +} BPyContextTempOverride; + +static void bpy_rna_context_temp_override__tp_dealloc(BPyContextTempOverride *self) +{ + PyObject_DEL(self); +} + +static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *self) +{ + bContext *C = self->context; + + CTX_py_state_push(C, &self->py_state, self->py_state_context_dict); + + self->ctx_init.win = CTX_wm_window(C); + self->ctx_init.win_is_set = (self->ctx_init.win != self->ctx_temp.win); + self->ctx_init.area = CTX_wm_area(C); + self->ctx_init.area_is_set = (self->ctx_init.area != self->ctx_temp.area); + self->ctx_init.region = CTX_wm_region(C); + self->ctx_init.region_is_set = (self->ctx_init.region != self->ctx_temp.region); + + wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win; + bScreen *screen = win ? WM_window_get_active_screen(win) : NULL; + ScrArea *area = self->ctx_temp.area_is_set ? self->ctx_temp.area : self->ctx_init.area; + ARegion *region = self->ctx_temp.region_is_set ? self->ctx_temp.region : self->ctx_init.region; + + /* Sanity check, the region is in the screen/area. */ + if (self->ctx_temp.region_is_set && (region != NULL)) { + if (area == NULL) { + PyErr_SetString(PyExc_TypeError, "Region set with NULL area"); + return NULL; + } + if ((screen && BLI_findindex(&screen->regionbase, region) == -1) && + (BLI_findindex(&area->regionbase, region) == -1)) { + PyErr_SetString(PyExc_TypeError, "Region not found in area"); + return NULL; + } + } + + if (self->ctx_temp.area_is_set && (area != NULL)) { + if (screen == NULL) { + PyErr_SetString(PyExc_TypeError, "Area set with NULL screen"); + return NULL; + } + if (BLI_findindex(&screen->areabase, area) == -1) { + PyErr_SetString(PyExc_TypeError, "Area not found in screen"); + return NULL; + } + } + + if (self->ctx_temp.win_is_set) { + CTX_wm_window_set(C, self->ctx_temp.win); + } + if (self->ctx_temp.area_is_set) { + CTX_wm_area_set(C, self->ctx_temp.area); + } + if (self->ctx_temp.region_is_set) { + CTX_wm_region_set(C, self->ctx_temp.region); + } + + Py_RETURN_NONE; +} + +static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self, + PyObject *UNUSED(args)) +{ + bContext *C = self->context; + + /* Special case where the window is expected to be freed on file-read, + * in this case the window should not be restored, see: T92818. */ + bool do_restore = true; + if (self->ctx_init.win) { + wmWindowManager *wm = CTX_wm_manager(C); + if (BLI_findindex(&wm->windows, self->ctx_init.win) == -1) { + CTX_wm_window_set(C, NULL); + do_restore = false; + } + } + + if (do_restore) { + if (self->ctx_init.win_is_set) { + CTX_wm_window_set(C, self->ctx_init.win); + } + if (self->ctx_init.area_is_set) { + CTX_wm_area_set(C, self->ctx_init.area); + } + if (self->ctx_init.region_is_set) { + CTX_wm_region_set(C, self->ctx_init.region); + } + } + + CTX_py_state_pop(C, &self->py_state); + Py_CLEAR(self->py_state_context_dict); + + Py_RETURN_NONE; +} + +static PyMethodDef bpy_rna_context_temp_override__tp_methods[] = { + {"__enter__", (PyCFunction)bpy_rna_context_temp_override_enter, METH_NOARGS}, + {"__exit__", (PyCFunction)bpy_rna_context_temp_override_exit, METH_VARARGS}, + {NULL}, +}; + +static PyTypeObject BPyContextTempOverride_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ContextTempOverride", + .tp_basicsize = sizeof(BPyContextTempOverride), + .tp_dealloc = (destructor)bpy_rna_context_temp_override__tp_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_methods = bpy_rna_context_temp_override__tp_methods, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Context Temporary Override Method + * \{ */ + +static PyObject *bpy_context_temp_override_extract_known_args(const char *const *kwds_static, + PyObject *kwds) +{ + PyObject *sentinel = Py_Ellipsis; + PyObject *kwds_parse = PyDict_New(); + for (int i = 0; kwds_static[i]; i++) { + PyObject *key = PyUnicode_FromString(kwds_static[i]); + PyObject *val = _PyDict_Pop(kwds, key, sentinel); + if (val != sentinel) { + if (PyDict_SetItem(kwds_parse, key, val) == -1) { + BLI_assert_unreachable(); + } + } + Py_DECREF(key); + Py_DECREF(val); + } + return kwds_parse; +} + +PyDoc_STRVAR(bpy_context_temp_override_doc, + ".. method:: temp_override(window, area, region, **keywords)\n" + "\n" + " Context manager to temporarily override members in the context.\n" + "\n" + " :arg window: Window override or None.\n" + " :type window: :class:`bpy.types.Window`\n" + " :arg area: Area override or None.\n" + " :type area: :class:`bpy.types.Area`\n" + " :arg region: Region override or None.\n" + " :type region: :class:`bpy.types.Region`\n" + " :arg keywords: Additional keywords override context members.\n" + " :return: The context manager .\n" + " :rtype: context manager\n"); +static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds) +{ + const PointerRNA *context_ptr = pyrna_struct_as_ptr(self, &RNA_Context); + if (context_ptr == NULL) { + return NULL; + } + + if (kwds == NULL) { + /* While this is effectively NOP, support having no keywords as it's more involved + * to return an alternative (dummy) context manager. */ + } + else { + /* Needed because the keywords copied into `kwds_parse` could contain anything. + * As the types of keys aren't checked. */ + if (!PyArg_ValidateKeywordArguments(kwds)) { + return NULL; + } + } + + struct { + struct BPy_StructRNA_Parse window; + struct BPy_StructRNA_Parse area; + struct BPy_StructRNA_Parse region; + } params = { + .window = {.type = &RNA_Window}, + .area = {.type = &RNA_Area}, + .region = {.type = &RNA_Region}, + }; + + static const char *const _keywords[] = {"window", "area", "region", NULL}; + static _PyArg_Parser _parser = { + "|$" /* Optional, keyword only arguments. */ + "O&" /* `window` */ + "O&" /* `area` */ + "O&" /* `region` */ + ":temp_override", + _keywords, + 0, + }; + /* Parse known keywords, the remaining keywords are set using #CTX_py_state_push. */ + kwds = kwds ? PyDict_Copy(kwds) : PyDict_New(); + { + PyObject *kwds_parse = bpy_context_temp_override_extract_known_args(_keywords, kwds); + const int parse_result = _PyArg_ParseTupleAndKeywordsFast(args, + kwds_parse, + &_parser, + pyrna_struct_as_ptr_or_null_parse, + ¶ms.window, + pyrna_struct_as_ptr_or_null_parse, + ¶ms.area, + pyrna_struct_as_ptr_or_null_parse, + ¶ms.region); + Py_DECREF(kwds_parse); + if (parse_result == -1) { + Py_DECREF(kwds); + return NULL; + } + } + + bContext *C = context_ptr->data; + { + /* Merge existing keys that don't exist in the keywords passed in. + * This makes it possible to nest context overrides. */ + PyObject *context_dict_current = CTX_py_dict_get(C); + if (context_dict_current != NULL) { + PyDict_Merge(kwds, context_dict_current, 0); + } + } + + ContextStore ctx_temp = {NULL}; + if (params.window.ptr != NULL) { + ctx_temp.win = params.window.ptr->data; + ctx_temp.win_is_set = true; + } + if (params.area.ptr != NULL) { + ctx_temp.area = params.area.ptr->data; + ctx_temp.area_is_set = true; + } + + if (params.region.ptr != NULL) { + ctx_temp.region = params.region.ptr->data; + ctx_temp.region_is_set = true; + } + + BPyContextTempOverride *ret = PyObject_New(BPyContextTempOverride, &BPyContextTempOverride_Type); + ret->context = C; + ret->ctx_temp = ctx_temp; + memset(&ret->ctx_init, 0, sizeof(ret->ctx_init)); + + ret->py_state_context_dict = kwds; + + return (PyObject *)ret; +} + +/** \} */ + +PyMethodDef BPY_rna_context_temp_override_method_def = { + "temp_override", + (PyCFunction)bpy_context_temp_override, + METH_VARARGS | METH_KEYWORDS, + bpy_context_temp_override_doc, +}; diff --git a/source/blender/python/intern/bpy_rna_context.h b/source/blender/python/intern/bpy_rna_context.h new file mode 100644 index 00000000000..ddd328131e6 --- /dev/null +++ b/source/blender/python/intern/bpy_rna_context.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup pythonintern + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyMethodDef BPY_rna_context_temp_override_method_def; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index a5299bc1616..376195ab845 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -22,6 +22,7 @@ #include "bpy_library.h" #include "bpy_rna.h" #include "bpy_rna_callback.h" +#include "bpy_rna_context.h" #include "bpy_rna_data.h" #include "bpy_rna_id_collection.h" #include "bpy_rna_text.h" @@ -159,6 +160,17 @@ static struct PyGetSetDef pyrna_windowmanager_getset[] = { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Context Type + * \{ */ + +static struct PyMethodDef pyrna_context_methods[] = { + {NULL, NULL, 0, NULL}, /* #BPY_rna_context_temp_override_method_def */ + {NULL, NULL, 0, NULL}, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Space Type * \{ */ @@ -254,6 +266,10 @@ void BPY_rna_types_extend_capi(void) /* WindowManager */ pyrna_struct_type_extend_capi( &RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset); + + /* Context */ + ARRAY_SET_ITEMS(pyrna_context_methods, BPY_rna_context_temp_override_method_def); + pyrna_struct_type_extend_capi(&RNA_Context, pyrna_context_methods, NULL); } /** \} */ diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h index da48599fdf9..56c66df5925 100644 --- a/source/blender/render/RE_bake.h +++ b/source/blender/render/RE_bake.h @@ -19,6 +19,8 @@ extern "C" { typedef struct BakeImage { struct Image *image; + int tile_number; + float uv_offset[2]; int width; int height; size_t offset; @@ -30,7 +32,7 @@ typedef struct BakeTargets { int images_num; /* Lookup table from Material number to BakeImage. */ - int *material_to_image; + struct Image **material_to_image; int materials_num; /* Pixel buffer to bake to. */ @@ -104,7 +106,8 @@ void RE_bake_margin(struct ImBuf *ibuf, int margin, char margin_type, struct Mesh const *me, - char const *uv_layer); + char const *uv_layer, + const float uv_offset[2]); void RE_bake_normal_world_to_object(const BakePixel pixel_array[], size_t pixels_num, diff --git a/source/blender/render/RE_texture_margin.h b/source/blender/render/RE_texture_margin.h index 85bd06b9940..0c91abeaddd 100644 --- a/source/blender/render/RE_texture_margin.h +++ b/source/blender/render/RE_texture_margin.h @@ -25,13 +25,18 @@ struct Mesh; * \param me: the mesh to use the polygons of. * \param uv_layer: The UV layer to use. */ -void RE_generate_texturemargin_adjacentfaces( - struct ImBuf *ibuf, char *mask, const int margin, struct Mesh const *me, char const *uv_layer); +void RE_generate_texturemargin_adjacentfaces(struct ImBuf *ibuf, + char *mask, + const int margin, + struct Mesh const *me, + char const *uv_layer, + const float uv_offset[2]); void RE_generate_texturemargin_adjacentfaces_dm(struct ImBuf *ibuf, char *mask, const int margin, - struct DerivedMesh *dm); + struct DerivedMesh *dm, + const float uv_offset[2]); #ifdef __cplusplus } diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 69235fb6cb1..5953c0f0f8f 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -146,12 +146,13 @@ void RE_bake_margin(ImBuf *ibuf, const int margin, const char margin_type, Mesh const *me, - char const *uv_layer) + char const *uv_layer, + const float uv_offset[2]) { /* margin */ switch (margin_type) { case R_BAKE_ADJACENT_FACES: - RE_generate_texturemargin_adjacentfaces(ibuf, mask, margin, me, uv_layer); + RE_generate_texturemargin_adjacentfaces(ibuf, mask, margin, me, uv_layer, uv_offset); break; default: /* fall through */ @@ -746,30 +747,36 @@ void RE_bake_pixels_populate(Mesh *me, for (int i = 0; i < tottri; i++) { const MLoopTri *lt = &looptri[i]; const MPoly *mp = &me->mpoly[lt->poly]; - float vec[3][2]; - int mat_nr = mp->mat_nr; - int image_id = targets->material_to_image[mat_nr]; - if (image_id < 0) { - continue; - } - - bd.bk_image = &targets->images[image_id]; bd.primitive_id = i; - for (int a = 0; a < 3; a++) { - const float *uv = mloopuv[lt->tri[a]].uv; + /* Find images matching this material. */ + Image *image = targets->material_to_image[mp->mat_nr]; + for (int image_id = 0; image_id < targets->images_num; image_id++) { + BakeImage *bk_image = &targets->images[image_id]; + if (bk_image->image != image) { + continue; + } - /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our - * intersection tests where a pixel gets in between 2 faces or the middle of a quad, - * camera aligned quads also have this problem but they are less common. - * Add a small offset to the UVs, fixes bug T18685. */ - vec[a][0] = uv[0] * (float)bd.bk_image->width - (0.5f + 0.001f); - vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f); - } + /* Compute triangle vertex UV coordinates. */ + float vec[3][2]; + for (int a = 0; a < 3; a++) { + const float *uv = mloopuv[lt->tri[a]].uv; + + /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our + * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + * camera aligned quads also have this problem but they are less common. + * Add a small offset to the UVs, fixes bug T18685. */ + vec[a][0] = (uv[0] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f); + vec[a][1] = (uv[1] - bk_image->uv_offset[1]) * (float)bk_image->height - (0.5f + 0.002f); + } - bake_differentials(&bd, vec[0], vec[1], vec[2]); - zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); + /* Rasterize triangle. */ + bd.bk_image = bk_image; + bake_differentials(&bd, vec[0], vec[1], vec[2]); + zspan_scanconvert( + &bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); + } } for (int i = 0; i < targets->images_num; i++) { diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index 33d961c027d..f93397eedab 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -51,7 +51,7 @@ typedef void (*MPassKnownData)(DerivedMesh *lores_dm, const int x, const int y); -typedef void *(*MInitBakeData)(MultiresBakeRender *bkr, Image *ima); +typedef void *(*MInitBakeData)(MultiresBakeRender *bkr, ImBuf *ibuf); typedef void (*MFreeBakeData)(void *bake_data); typedef struct MultiresBakeResult { @@ -64,6 +64,7 @@ typedef struct { MPoly *mpoly; MLoop *mloop; MLoopUV *mloopuv; + float uv_offset[2]; const MLoopTri *mlooptri; float *pvtangent; const float (*precomputed_normals)[3]; @@ -91,7 +92,6 @@ typedef struct { typedef struct { float *heights; - Image *ima; DerivedMesh *ssdm; const int *orig_index_mp_to_orig; } MHeightBakeData; @@ -148,7 +148,8 @@ static void init_bake_rast(MBakeRast *bake_rast, static void flush_pixel(const MResolvePixelData *data, const int x, const int y) { - const float st[2] = {(x + 0.5f) / data->w, (y + 0.5f) / data->h}; + const float st[2] = {(x + 0.5f) / data->w + data->uv_offset[0], + (y + 0.5f) / data->h + data->uv_offset[1]}; const float *st0, *st1, *st2; const float *tang0, *tang1, *tang2; float no0[3], no1[3], no2[3]; @@ -395,8 +396,12 @@ static void *do_multires_bake_thread(void *data_v) data->tri_index = tri_index; - bake_rasterize( - bake_rast, mloopuv[lt->tri[0]].uv, mloopuv[lt->tri[1]].uv, mloopuv[lt->tri[2]].uv); + float uv[3][2]; + sub_v2_v2v2(uv[0], mloopuv[lt->tri[0]].uv, data->uv_offset); + sub_v2_v2v2(uv[1], mloopuv[lt->tri[1]].uv, data->uv_offset); + sub_v2_v2v2(uv[2], mloopuv[lt->tri[2]].uv, data->uv_offset); + + bake_rasterize(bake_rast, uv[0], uv[1], uv[2]); /* tag image buffer for refresh */ if (data->ibuf->rect_float) { @@ -447,6 +452,8 @@ static void init_ccgdm_arrays(DerivedMesh *dm) static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, + ImageTile *tile, + ImBuf *ibuf, bool require_tangent, MPassKnownData passKnownData, MInitBakeData initBakeData, @@ -462,7 +469,6 @@ static void do_multires_bake(MultiresBakeRender *bkr, MultiresBakeThread *handles; MultiresBakeQueue queue; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); MVert *mvert = dm->getVertArray(dm); MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); @@ -511,7 +517,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, /* all threads shares the same custom bake data */ if (initBakeData) { - bake_data = initBakeData(bkr, ima); + bake_data = initBakeData(bkr, ibuf); } if (tot_thread > 1) { @@ -539,6 +545,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, 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; @@ -590,8 +597,6 @@ static void do_multires_bake(MultiresBakeRender *bkr, MEM_freeN(handles); BKE_id_free(NULL, temp_mesh); - - BKE_image_release_ibuf(ima, ibuf, NULL); } } @@ -758,10 +763,9 @@ static void interp_barycentric_mlooptri(DerivedMesh *dm, /* **************** Displacement Baker **************** */ -static void *init_heights_data(MultiresBakeRender *bkr, Image *ima) +static void *init_heights_data(MultiresBakeRender *bkr, ImBuf *ibuf) { MHeightBakeData *height_data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); DerivedMesh *lodm = bkr->lores_dm; BakeImBufuserData *userdata = ibuf->userdata; @@ -772,7 +776,6 @@ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima) height_data = MEM_callocN(sizeof(MHeightBakeData), "MultiresBake heightData"); - height_data->ima = ima; height_data->heights = userdata->displacement_buffer; if (!bkr->use_lores_mesh) { @@ -794,8 +797,6 @@ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima) height_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX); - BKE_image_release_ibuf(ima, ibuf, NULL); - return (void *)height_data; } @@ -903,7 +904,7 @@ static void apply_heights_callback(DerivedMesh *lores_dm, /* **************** Normal Maps Baker **************** */ -static void *init_normal_data(MultiresBakeRender *bkr, Image *UNUSED(ima)) +static void *init_normal_data(MultiresBakeRender *bkr, ImBuf *UNUSED(ibuf)) { MNormalBakeData *normal_data; DerivedMesh *lodm = bkr->lores_dm; @@ -1099,7 +1100,7 @@ static void create_ao_raytree(MultiresBakeRender *bkr, MAOBakeData *ao_data) RE_rayobject_done(raytree); } -static void *init_ao_data(MultiresBakeRender *bkr, Image *UNUSED(ima)) +static void *init_ao_data(MultiresBakeRender *bkr, ImBuf *UNUSED(ibuf)) { MAOBakeData *ao_data; DerivedMesh *lodm = bkr->lores_dm; @@ -1313,8 +1314,12 @@ static void apply_ao_callback(DerivedMesh *lores_dm, /* ******$***************** Post processing ************************* */ -static void bake_ibuf_filter( - ImBuf *ibuf, char *mask, const int margin, const char margin_type, DerivedMesh *dm) +static void bake_ibuf_filter(ImBuf *ibuf, + char *mask, + const int margin, + const char margin_type, + DerivedMesh *dm, + const float uv_offset[2]) { /* must check before filtering */ const bool is_new_alpha = (ibuf->planes != R_IMF_PLANES_RGBA) && BKE_imbuf_alpha_test(ibuf); @@ -1322,7 +1327,7 @@ static void bake_ibuf_filter( if (margin) { switch (margin_type) { case R_BAKE_ADJACENT_FACES: - RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm); + RE_generate_texturemargin_adjacentfaces_dm(ibuf, mask, margin, dm, uv_offset); break; default: /* fall through */ @@ -1427,38 +1432,54 @@ static void bake_images(MultiresBakeRender *bkr, MultiresBakeResult *result) for (link = bkr->image.first; link; link = link->next) { Image *ima = (Image *)link->data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - - if (ibuf->x > 0 && ibuf->y > 0) { - BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData), - "MultiresBake userdata"); - userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask"); - ibuf->userdata = userdata; - - switch (bkr->mode) { - case RE_BAKE_NORMALS: - do_multires_bake( - bkr, ima, true, apply_tangmat_callback, init_normal_data, free_normal_data, result); - break; - case RE_BAKE_DISPLACEMENT: - do_multires_bake(bkr, - ima, - false, - apply_heights_callback, - init_heights_data, - free_heights_data, - result); - break; -/* TODO: restore ambient occlusion baking support. */ + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; + + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + + if (ibuf->x > 0 && ibuf->y > 0) { + BakeImBufuserData *userdata = MEM_callocN(sizeof(BakeImBufuserData), + "MultiresBake userdata"); + userdata->mask_buffer = MEM_callocN(ibuf->y * ibuf->x, "MultiresBake imbuf mask"); + ibuf->userdata = userdata; + + switch (bkr->mode) { + case RE_BAKE_NORMALS: + do_multires_bake(bkr, + ima, + tile, + ibuf, + true, + apply_tangmat_callback, + init_normal_data, + free_normal_data, + result); + break; + case RE_BAKE_DISPLACEMENT: + do_multires_bake(bkr, + ima, + tile, + ibuf, + false, + apply_heights_callback, + init_heights_data, + free_heights_data, + result); + break; + /* TODO: restore ambient occlusion baking support. */ #if 0 - case RE_BAKE_AO: - do_multires_bake(bkr, ima, false, apply_ao_callback, init_ao_data, free_ao_data, result); - break; + case RE_BAKE_AO: + do_multires_bake(bkr, ima, tile, ibuf, false, apply_ao_callback, init_ao_data, free_ao_data, result); + break; #endif + } } - } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, NULL); + } ima->id.tag |= LIB_TAG_DOIT; } @@ -1471,48 +1492,62 @@ static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result) for (link = bkr->image.first; link; link = link->next) { Image *ima = (Image *)link->data; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; - if (ibuf->x <= 0 || ibuf->y <= 0) { - continue; - } + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + ImageUser iuser; + BKE_imageuser_default(&iuser); + iuser.tile = tile->tile_number; - if (use_displacement_buffer) { - bake_ibuf_normalize_displacement(ibuf, - userdata->displacement_buffer, - userdata->mask_buffer, - result->height_min, - result->height_max); - } + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + BakeImBufuserData *userdata = (BakeImBufuserData *)ibuf->userdata; - bake_ibuf_filter( - ibuf, userdata->mask_buffer, bkr->bake_margin, bkr->bake_margin_type, bkr->lores_dm); + if (ibuf->x <= 0 || ibuf->y <= 0) { + continue; + } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - BKE_image_mark_dirty(ima, ibuf); + if (use_displacement_buffer) { + bake_ibuf_normalize_displacement(ibuf, + userdata->displacement_buffer, + userdata->mask_buffer, + result->height_min, + result->height_max); + } - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; - } + float uv_offset[2]; + BKE_image_get_tile_uv(ima, tile->tile_number, uv_offset); - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; - imb_freemipmapImBuf(ibuf); - } + bake_ibuf_filter(ibuf, + userdata->mask_buffer, + bkr->bake_margin, + bkr->bake_margin_type, + bkr->lores_dm, + uv_offset); + + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + BKE_image_mark_dirty(ima, ibuf); - if (ibuf->userdata) { - if (userdata->displacement_buffer) { - MEM_freeN(userdata->displacement_buffer); + if (ibuf->rect_float) { + ibuf->userflags |= IB_RECT_INVALID; } - MEM_freeN(userdata->mask_buffer); - MEM_freeN(userdata); - ibuf->userdata = NULL; - } + if (ibuf->mipmap[0]) { + ibuf->userflags |= IB_MIPMAP_INVALID; + imb_freemipmapImBuf(ibuf); + } + + if (ibuf->userdata) { + if (userdata->displacement_buffer) { + MEM_freeN(userdata->displacement_buffer); + } - BKE_image_release_ibuf(ima, ibuf, NULL); - DEG_id_tag_update(&ima->id, 0); + MEM_freeN(userdata->mask_buffer); + MEM_freeN(userdata); + ibuf->userdata = NULL; + } + + BKE_image_release_ibuf(ima, ibuf, NULL); + DEG_id_tag_update(&ima->id, 0); + } } } diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index d01c0dbea71..2d68148a86a 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -46,6 +46,7 @@ class TextureMarginMap { Vector<int> loop_to_poly_map_; int w_, h_; + float uv_offset_[2]; Vector<uint32_t> pixel_data_; ZSpan zspan_; uint32_t value_to_store_; @@ -61,6 +62,7 @@ class TextureMarginMap { public: TextureMarginMap(size_t w, size_t h, + const float uv_offset[2], MPoly const *mpoly, MLoop const *mloop, MLoopUV const *mloopuv, @@ -76,6 +78,8 @@ class TextureMarginMap { totloop_(totloop), totedge_(totedge) { + copy_v2_v2(uv_offset_, uv_offset); + pixel_data_.resize(w_ * h_, 0xFFFFFFFF); zbuf_alloc_span(&zspan_, w_, h_); @@ -277,8 +281,8 @@ class TextureMarginMap { float2 uv_to_xy(MLoopUV const &mloopuv) const { float2 ret; - ret.x = ((mloopuv.uv[0] * w_) - (0.5f + 0.001f)); - ret.y = ((mloopuv.uv[1] * h_) - (0.5f + 0.001f)); + ret.x = (((mloopuv.uv[0] - uv_offset_[0]) * w_) - (0.5f + 0.001f)); + ret.y = (((mloopuv.uv[1] - uv_offset_[1]) * h_) - (0.5f + 0.001f)); return ret; } @@ -482,7 +486,8 @@ static void generate_margin(ImBuf *ibuf, const int margin, const Mesh *me, DerivedMesh *dm, - char const *uv_layer) + char const *uv_layer, + const float uv_offset[2]) { MPoly *mpoly; @@ -531,7 +536,8 @@ static void generate_margin(ImBuf *ibuf, tottri = dm->getNumLoopTri(dm); } - TextureMarginMap map(ibuf->x, ibuf->y, mpoly, mloop, mloopuv, totpoly, totloop, totedge); + TextureMarginMap map( + ibuf->x, ibuf->y, uv_offset, mpoly, mloop, mloopuv, totpoly, totloop, totedge); bool draw_new_mask = false; /* Now the map contains 3 sorts of values: 0xFFFFFFFF for empty pixels, `0x80000000 + polyindex` @@ -555,8 +561,8 @@ static void generate_margin(ImBuf *ibuf, * intersection tests where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug T18685. */ - vec[a][0] = uv[0] * (float)ibuf->x - (0.5f + 0.001f); - vec[a][1] = uv[1] * (float)ibuf->y - (0.5f + 0.002f); + vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f); + vec[a][1] = (uv[1] - uv_offset[1]) * (float)ibuf->y - (0.5f + 0.002f); } /* NOTE: we need the top bit for the dijkstra distance map. */ @@ -592,16 +598,20 @@ static void generate_margin(ImBuf *ibuf, } // namespace blender::render::texturemargin -void RE_generate_texturemargin_adjacentfaces( - ImBuf *ibuf, char *mask, const int margin, const Mesh *me, char const *uv_layer) +void RE_generate_texturemargin_adjacentfaces(ImBuf *ibuf, + char *mask, + const int margin, + const Mesh *me, + char const *uv_layer, + const float uv_offset[2]) { - blender::render::texturemargin::generate_margin(ibuf, mask, margin, me, nullptr, uv_layer); + blender::render::texturemargin::generate_margin( + ibuf, mask, margin, me, nullptr, uv_layer, uv_offset); } -void RE_generate_texturemargin_adjacentfaces_dm(ImBuf *ibuf, - char *mask, - const int margin, - DerivedMesh *dm) +void RE_generate_texturemargin_adjacentfaces_dm( + ImBuf *ibuf, char *mask, const int margin, DerivedMesh *dm, const float uv_offset[2]) { - blender::render::texturemargin::generate_margin(ibuf, mask, margin, nullptr, dm, nullptr); + blender::render::texturemargin::generate_margin( + ibuf, mask, margin, nullptr, dm, nullptr, uv_offset); } diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index bb313d4962c..8ba3bac7cad 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -277,11 +277,12 @@ static void pointdensity_cache_vertex_color(PointDensity *pd, BLI_assert(data_color); - if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPCOL)) { + if (!CustomData_has_layer(&mesh->ldata, CD_PROP_BYTE_COLOR)) { return; } - CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPCOL, pd->vertex_attribute_name, layername); - mcol = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPCOL, layername); + 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); if (!mcol) { return; } @@ -372,7 +373,7 @@ static void pointdensity_cache_object(PointDensity *pd, Object *ob) mask.fmask |= CD_MASK_MTFACE | CD_MASK_MCOL; switch (pd->ob_color_source) { case TEX_PD_COLOR_VERTCOL: - mask.lmask |= CD_MASK_MLOOPCOL; + mask.lmask |= CD_MASK_PROP_BYTE_COLOR; break; case TEX_PD_COLOR_VERTWEIGHT: mask.vmask |= CD_MASK_MDEFORMVERT; diff --git a/source/blender/sequencer/SEQ_channels.h b/source/blender/sequencer/SEQ_channels.h index 1d87875fb26..9436d5dfa32 100644 --- a/source/blender/sequencer/SEQ_channels.h +++ b/source/blender/sequencer/SEQ_channels.h @@ -15,6 +15,7 @@ struct Editing; struct ListBase; struct Scene; struct SeqTimelineChannel; +struct Sequence; struct ListBase *SEQ_channels_displayed_get(struct Editing *ed); void SEQ_channels_displayed_set(struct Editing *ed, struct ListBase *channels); @@ -28,6 +29,7 @@ char *SEQ_channel_name_get(struct ListBase *channels, const int channel_index); bool SEQ_channel_is_locked(const struct SeqTimelineChannel *channel); bool SEQ_channel_is_muted(const struct SeqTimelineChannel *channel); int SEQ_channel_index_get(const struct SeqTimelineChannel *channel); +ListBase *SEQ_get_channels_by_seq(struct ListBase *seqbase, const struct Sequence *seq); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_relations.h b/source/blender/sequencer/SEQ_relations.h index 735b5659ca9..917f549f16d 100644 --- a/source/blender/sequencer/SEQ_relations.h +++ b/source/blender/sequencer/SEQ_relations.h @@ -19,6 +19,10 @@ struct Scene; struct Sequence; /** + * Check if one sequence is input to the other. + */ +bool SEQ_relation_is_effect_of_strip(const struct Sequence *effect, const struct Sequence *input); +/** * Function to free imbuf and anim data on changes. */ void SEQ_relations_sequence_free_anim(struct Sequence *seq); @@ -64,6 +68,7 @@ void SEQ_cache_iterate( struct Sequence *SEQ_find_metastrip_by_sequence(ListBase *seqbase /* = ed->seqbase */, struct Sequence *meta /* = NULL */, struct Sequence *seq); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/SEQ_render.h b/source/blender/sequencer/SEQ_render.h index bfe10d3eae9..a74eba5fc6f 100644 --- a/source/blender/sequencer/SEQ_render.h +++ b/source/blender/sequencer/SEQ_render.h @@ -17,6 +17,7 @@ struct ListBase; struct Main; struct Scene; struct Sequence; +struct rctf; typedef enum eSeqTaskId { SEQ_TASK_MAIN_RENDER, @@ -64,7 +65,6 @@ struct ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, void SEQ_render_thumbnails(const struct SeqRenderData *context, struct Sequence *seq, struct Sequence *seq_orig, - float start_frame, float frame_step, rctf *view_area, const short *stop); @@ -77,6 +77,18 @@ struct ImBuf *SEQ_get_thumbnail(const struct SeqRenderData *context, rcti *crop, bool clipped); /** + * Get frame for first thumbnail. + */ +float SEQ_render_thumbnail_first_frame_get(struct Sequence *seq, + float frame_step, + struct rctf *view_area); +/** + * Get frame for first thumbnail. + */ +float SEQ_render_thumbnail_next_frame_get(struct Sequence *seq, + float last_frame, + float frame_step); +/** * Get frame step for equally spaced thumbnails. These thumbnails should always be present in * memory, so they can be used when zooming. */ diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index eb910a5a5d1..a342dfe10a2 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -119,6 +119,22 @@ void SEQ_image_preview_unit_from_px(const struct Scene *scene, const float co_src[2], float co_dst[2]); +/** + * Get viewport axis aligned bounding box from a collection of sequences. + * The collection must have one or more strips + * + * \param scene: Scene in which strips are located + * \param strips: Collection of strips to get the bounding box from + * \param apply_rotation: Include sequence rotation transform in the bounding box calculation + * \param r_min: Minimum x and y values + * \param r_max: Maximum x and y values + */ +void SEQ_image_transform_bounding_box_from_collection(struct Scene *scene, + struct SeqCollection *strips, + bool apply_rotation, + float r_min[2], + float r_max[2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/channels.c b/source/blender/sequencer/intern/channels.c index e8e82af03f5..21e3461c7d0 100644 --- a/source/blender/sequencer/intern/channels.c +++ b/source/blender/sequencer/intern/channels.c @@ -81,3 +81,19 @@ bool SEQ_channel_is_muted(const SeqTimelineChannel *channel) { return (channel->flag & SEQ_CHANNEL_MUTE) != 0; } + +ListBase *SEQ_get_channels_by_seq(ListBase *seqbase, const Sequence *seq) +{ + ListBase *lb = NULL; + + LISTBASE_FOREACH (Sequence *, iseq, seqbase) { + if (seq == iseq) { + return seqbase; + } + if ((lb = SEQ_get_channels_by_seq(&iseq->seqbase, seq))) { + return lb; + } + } + + return NULL; +} diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 0192f4f625c..f4fc79a6572 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -44,6 +44,7 @@ #include "RE_pipeline.h" +#include "SEQ_channels.h" #include "SEQ_effects.h" #include "SEQ_proxy.h" #include "SEQ_relations.h" @@ -2421,8 +2422,6 @@ static ImBuf *do_multicam(const SeqRenderData *context, { ImBuf *out; Editing *ed; - ListBase *seqbasep; - ListBase *channels = &seq->channels; if (seq->multicam_source == 0 || seq->multicam_source >= seq->machine) { return NULL; @@ -2432,7 +2431,8 @@ static ImBuf *do_multicam(const SeqRenderData *context, if (!ed) { return NULL; } - seqbasep = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + ListBase *seqbasep = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, seq); if (!seqbasep) { return NULL; } @@ -2463,13 +2463,12 @@ static int early_out_adjustment(Sequence *UNUSED(seq), float UNUSED(fac)) static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, float timeline_frame) { Editing *ed; - ListBase *seqbasep; - ListBase *channels = &seq->channels; ImBuf *i = NULL; ed = context->scene->ed; - seqbasep = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + ListBase *seqbasep = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, seq); /* Clamp timeline_frame to strip range so it behaves as if it had "still frame" offset (last * frame is static after end of strip). This is how most strips behave. This way transition diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index a4d8cf79d1f..2710edd6e80 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -20,6 +20,7 @@ #include "BKE_scene.h" #include "SEQ_iterator.h" +#include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_time.h" #include "render.h" @@ -241,15 +242,6 @@ static void collection_filter_channel_up_to_incl(SeqCollection *collection, cons } } -static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) -{ - if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || - seq_effect->seq3 == possibly_input) { - return true; - } - return false; -} - /* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. * Order of applying these conditions is important. */ static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) @@ -262,7 +254,8 @@ static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_time return false; } - if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { + if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && + SEQ_relation_is_effect_of_strip(seq_iter, seq)) { /* Strips in same channel or higher than its effect are rendered. */ if (seq->machine >= seq_iter->machine) { return true; diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 18b0794dc72..8d8a13be09e 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1992,6 +1992,32 @@ ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, return ibuf; } +float SEQ_render_thumbnail_first_frame_get(Sequence *seq, float frame_step, rctf *view_area) +{ + int first_drawable_frame = max_iii(seq->startdisp, seq->start, view_area->xmin); + + /* First frame should correspond to handle position. */ + if (first_drawable_frame == seq->startdisp) { + return seq->startdisp; + } + + float aligned_frame_offset = (int)((first_drawable_frame - seq->start) / frame_step) * + frame_step; + return seq->start + aligned_frame_offset; +} + +float SEQ_render_thumbnail_next_frame_get(Sequence *seq, float last_frame, float frame_step) +{ + float next_frame = last_frame + frame_step; + + /* If handle position was displayed, align next frame with `seq->start`. */ + if (last_frame == seq->startdisp) { + next_frame = seq->start + ((int)((last_frame - seq->start) / frame_step) + 1) * frame_step; + } + + return next_frame; +} + /* Gets the direct image from source and scales to thumbnail size. */ static ImBuf *seq_get_uncached_thumbnail(const SeqRenderData *context, SeqRenderState *state, @@ -2053,7 +2079,6 @@ ImBuf *SEQ_get_thumbnail( void SEQ_render_thumbnails(const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, - float start_frame, float frame_step, rctf *view_area, const short *stop) @@ -2063,24 +2088,24 @@ 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. */ - start_frame = start_frame - frame_step; float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; upper_thumb_bound = (upper_thumb_bound > view_area->xmax) ? view_area->xmax + frame_step : upper_thumb_bound; - while ((start_frame < upper_thumb_bound) & !*stop) { + float timeline_frame = SEQ_render_thumbnail_first_frame_get(seq, frame_step, view_area); + while ((timeline_frame < upper_thumb_bound) & !*stop) { ImBuf *ibuf = seq_cache_get( - context, seq_orig, round_fl_to_int(start_frame), SEQ_CACHE_STORE_THUMBNAIL); + context, seq_orig, round_fl_to_int(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL); if (ibuf) { IMB_freeImBuf(ibuf); - start_frame += frame_step; + timeline_frame = SEQ_render_thumbnail_next_frame_get(seq, timeline_frame, frame_step); continue; } - ibuf = seq_get_uncached_thumbnail(context, &state, seq, round_fl_to_int(start_frame)); + ibuf = seq_get_uncached_thumbnail(context, &state, seq, round_fl_to_int(timeline_frame)); if (ibuf) { - seq_cache_thumbnail_put(context, seq_orig, round_fl_to_int(start_frame), ibuf, view_area); + seq_cache_thumbnail_put(context, seq_orig, round_fl_to_int(timeline_frame), ibuf, view_area); IMB_freeImBuf(ibuf); seq_orig->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS; } @@ -2090,7 +2115,7 @@ void SEQ_render_thumbnails(const SeqRenderData *context, return; } - start_frame += frame_step; + timeline_frame = SEQ_render_thumbnail_next_frame_get(seq, timeline_frame, frame_step); } } diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 7aa81f5ae8a..6c7bb71cb75 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -154,8 +154,7 @@ static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Se } /* Remove effects, that use seq. */ - if ((user_seq->seq1 && user_seq->seq1 == seq) || (user_seq->seq2 && user_seq->seq2 == seq) || - (user_seq->seq3 && user_seq->seq3 == seq)) { + if (SEQ_relation_is_effect_of_strip(user_seq, seq)) { user_seq->flag |= SEQ_FLAG_DELETE; /* Strips can be used as mask even if not in same seqbase. */ sequencer_flag_users_for_removal(scene, &scene->ed->seqbase, user_seq); diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index a65a331c650..1899cc99263 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -34,10 +34,15 @@ #include "image_cache.h" #include "utils.h" +bool SEQ_relation_is_effect_of_strip(const Sequence *effect, const Sequence *input) +{ + return ELEM(input, effect->seq1, effect->seq2); +} + /* check whether sequence cur depends on seq */ static bool seq_relations_check_depend(Sequence *seq, Sequence *cur) { - if (cur->seq1 == seq || cur->seq2 == seq || cur->seq3 == seq) { + if (SEQ_relation_is_effect_of_strip(cur, seq)) { return true; } diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 087e2610bd6..2c9ab0a3335 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -297,6 +297,9 @@ static int shuffle_seq_time_offset_test(SeqCollection *strips_to_shuffle, if (!SEQ_transform_test_overlap_seq_seq(seq, seq_other)) { continue; } + if (SEQ_relation_is_effect_of_strip(seq_other, seq)) { + continue; + } if (UNLIKELY(SEQ_collection_has_strip(seq_other, strips_to_shuffle))) { CLOG_WARN(&LOG, "Strip overlaps with itself or another strip, that is to be shuffled. " @@ -517,3 +520,18 @@ void SEQ_image_preview_unit_from_px(const Scene *scene, const float co_src[2], f co_dst[0] = co_src[0] / scene->r.xsch; co_dst[1] = co_src[1] / scene->r.ysch; } + +void SEQ_image_transform_bounding_box_from_collection( + Scene *scene, SeqCollection *strips, bool apply_rotation, float r_min[2], float r_max[2]) +{ + Sequence *seq; + + INIT_MINMAX2(r_min, r_max); + SEQ_ITERATOR_FOREACH (seq, strips) { + float quad[4][2]; + SEQ_image_transform_quad_get(scene, seq, apply_rotation, quad); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(r_min, r_max, quad[i]); + } + } +} diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h index ef6bee0c1fe..992c7a9b2ec 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h @@ -124,7 +124,7 @@ struct wmGizmoMap { /** * This is a container for all gizmo types that can be instantiated in a region. - * (similar to dropboxes). + * (similar to drop-boxes). * * \note There is only ever one of these for every (area, region) combination. */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index d17afad0918..7457358698d 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -896,16 +896,32 @@ elseif(WIN32) endif() if(WITH_CODEC_FFMPEG) - install( - FILES - ${LIBDIR}/ffmpeg/lib/avcodec-58.dll - ${LIBDIR}/ffmpeg/lib/avformat-58.dll - ${LIBDIR}/ffmpeg/lib/avdevice-58.dll - ${LIBDIR}/ffmpeg/lib/avutil-56.dll - ${LIBDIR}/ffmpeg/lib/swscale-5.dll - ${LIBDIR}/ffmpeg/lib/swresample-3.dll - DESTINATION "." - ) + # Filenames change slightly between ffmpeg versions + # check both 5.0 and fallback to 4.4 to ease the transition + # between versions. + if(EXISTS "${LIBDIR}/ffmpeg/lib/avcodec-59.dll") + install( + FILES + ${LIBDIR}/ffmpeg/lib/avcodec-59.dll + ${LIBDIR}/ffmpeg/lib/avformat-59.dll + ${LIBDIR}/ffmpeg/lib/avdevice-59.dll + ${LIBDIR}/ffmpeg/lib/avutil-57.dll + ${LIBDIR}/ffmpeg/lib/swscale-6.dll + ${LIBDIR}/ffmpeg/lib/swresample-4.dll + DESTINATION "." + ) + else() + install( + FILES + ${LIBDIR}/ffmpeg/lib/avcodec-58.dll + ${LIBDIR}/ffmpeg/lib/avformat-58.dll + ${LIBDIR}/ffmpeg/lib/avdevice-58.dll + ${LIBDIR}/ffmpeg/lib/avutil-56.dll + ${LIBDIR}/ffmpeg/lib/swscale-5.dll + ${LIBDIR}/ffmpeg/lib/swresample-3.dll + DESTINATION "." + ) + endif() endif() if(WITH_TBB) install( diff --git a/source/tools b/source/tools -Subproject 4c1e01e3e309282beb1af3b1eddb2c7f9a666b5 +Subproject 1e658ca996f11e5ff3398d89bd81f5b719304a5 |