Initial Grease Pencil 3.0 stage #106848
|
@ -571,4 +571,8 @@ class KeyframesCo:
|
|||
keyframe_points.foreach_set("co", co_buffer)
|
||||
keyframe_points.foreach_set("interpolation", ipo_buffer)
|
||||
|
||||
fcurve.update()
|
||||
# TODO: in Blender 4.0 the next lines can be replaced with one call to `fcurve.update()`.
|
||||
# See https://projects.blender.org/blender/blender/issues/107126 for more info.
|
||||
keyframe_points.sort()
|
||||
keyframe_points.deduplicate()
|
||||
keyframe_points.handles_recalc()
|
||||
|
|
|
@ -416,6 +416,7 @@ int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);
|
|||
* Move the indexed keyframe to the given value,
|
||||
* and move the handles with it to ensure the slope remains the same.
|
||||
*/
|
||||
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time);
|
||||
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, float new_value);
|
||||
|
||||
/* .............. */
|
||||
|
@ -472,6 +473,14 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
|
|||
struct BezTriple *next,
|
||||
float *r_pdelta);
|
||||
|
||||
/**
|
||||
* Resize the FCurve 'bezt' array to fit the given length.
|
||||
*
|
||||
* \param new_totvert new number of elements in the FCurve's `bezt` array.
|
||||
* Constraint: `0 <= new_totvert <= fcu->totvert`
|
||||
*/
|
||||
void BKE_fcurve_bezt_shrink(struct FCurve *fcu, int new_totvert);
|
||||
|
||||
/**
|
||||
* Delete a keyframe from an F-curve at a specific index.
|
||||
*/
|
||||
|
@ -499,6 +508,21 @@ void BKE_fcurve_merge_duplicate_keys(struct FCurve *fcu,
|
|||
const int sel_flag,
|
||||
const bool use_handle);
|
||||
|
||||
/**
|
||||
* Ensure the FCurve is a proper function, such that every X-coordinate of the
|
||||
* timeline has only one value of the FCurve. In other words, removes duplicate
|
||||
* keyframes.
|
||||
*
|
||||
* Contrary to #BKE_fcurve_merge_duplicate_keys, which is intended for
|
||||
* interactive use, and where selection matters, this is a simpler deduplication
|
||||
* where the last duplicate "wins".
|
||||
*
|
||||
* Assumes the keys are sorted (see #sort_time_fcurve).
|
||||
*
|
||||
* After deduplication, call `BKE_fcurve_handles_recalc(fcu);`
|
||||
*/
|
||||
void BKE_fcurve_deduplicate_keys(struct FCurve *fcu);
|
||||
|
||||
/* -------- Curve Sanity -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -883,6 +883,14 @@ int BKE_fcurve_active_keyframe_index(const FCurve *fcu)
|
|||
|
||||
/** \} */
|
||||
|
||||
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time)
|
||||
{
|
||||
const float time_delta = new_time - keyframe->vec[1][0];
|
||||
keyframe->vec[0][0] += time_delta;
|
||||
keyframe->vec[1][0] = new_time;
|
||||
keyframe->vec[2][0] += time_delta;
|
||||
}
|
||||
|
||||
void BKE_fcurve_keyframe_move_value_with_handles(struct BezTriple *keyframe, const float new_value)
|
||||
{
|
||||
const float value_delta = new_value - keyframe->vec[1][1];
|
||||
|
@ -1659,6 +1667,24 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
|
|||
return true;
|
||||
}
|
||||
|
||||
void BKE_fcurve_bezt_shrink(struct FCurve *fcu, const int new_totvert)
|
||||
{
|
||||
BLI_assert(new_totvert >= 0);
|
||||
BLI_assert(new_totvert <= fcu->totvert);
|
||||
|
||||
/* No early return when new_totvert == fcu->totvert. There is no way to know the intention of the
|
||||
* caller, nor the history of the FCurve so far, so `fcu->bezt` may actually have allocated space
|
||||
* for more than `fcu->totvert` keys. */
|
||||
|
||||
if (new_totvert == 0) {
|
||||
fcurve_bezt_free(fcu);
|
||||
return;
|
||||
}
|
||||
|
||||
fcu->bezt = MEM_reallocN(fcu->bezt, new_totvert * sizeof(*(fcu->bezt)));
|
||||
fcu->totvert = new_totvert;
|
||||
}
|
||||
|
||||
void BKE_fcurve_delete_key(FCurve *fcu, int index)
|
||||
{
|
||||
/* sanity check */
|
||||
|
@ -1849,6 +1875,52 @@ void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool
|
|||
BLI_freelistN(&retained_keys);
|
||||
}
|
||||
|
||||
void BKE_fcurve_deduplicate_keys(FCurve *fcu)
|
||||
{
|
||||
BLI_assert_msg(fcu->bezt, "this function only works with regular (non-sampled) FCurves");
|
||||
if (fcu->totvert < 2 || fcu->bezt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int prev_bezt_index = 0;
|
||||
for (int i = 1; i < fcu->totvert; i++) {
|
||||
BezTriple *bezt = &fcu->bezt[i];
|
||||
BezTriple *prev_bezt = &fcu->bezt[prev_bezt_index];
|
||||
|
||||
const float bezt_x = bezt->vec[1][0];
|
||||
const float prev_x = prev_bezt->vec[1][0];
|
||||
|
||||
if (bezt_x - prev_x <= BEZT_BINARYSEARCH_THRESH) {
|
||||
/* Replace 'prev_bezt', as it has the same X-coord as 'bezt' and the last one wins. */
|
||||
*prev_bezt = *bezt;
|
||||
|
||||
if (floor(bezt_x) == bezt_x) {
|
||||
/* Keep the 'bezt_x' coordinate, as being on a frame is more desirable
|
||||
* than being ever so slightly off. */
|
||||
}
|
||||
else {
|
||||
/* Move the retained key to the old X-coordinate, to 'anchor' the X-coordinate used for
|
||||
* subsequente comparisons. Without this, the reference X-coordinate would keep moving
|
||||
* forward in time, potentially merging in more keys than desired. */
|
||||
BKE_fcurve_keyframe_move_time_with_handles(prev_bezt, prev_x);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Next iteration should look at the current element. However, because of the deletions, that
|
||||
* may not be at index 'i'; after this increment, `prev_bezt_index` points at where the current
|
||||
* element should go. */
|
||||
prev_bezt_index++;
|
||||
|
||||
if (prev_bezt_index != i) {
|
||||
/* This bezt should be kept, so copy it to its new location in the array. */
|
||||
fcu->bezt[prev_bezt_index] = *bezt;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_fcurve_bezt_shrink(fcu, prev_bezt_index + 1);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "DNA_anim_types.h"
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
namespace blender::bke::tests {
|
||||
|
||||
/* Epsilon for floating point comparisons. */
|
||||
|
@ -346,6 +348,37 @@ TEST(BKE_fcurve, BKE_fcurve_keyframe_move_value_with_handles)
|
|||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, BKE_fcurve_keyframe_move_time_with_handles)
|
||||
{
|
||||
FCurve *fcu = BKE_fcurve_create();
|
||||
|
||||
insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||
insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||
insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF);
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][0], 5.2671194f);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][1], 15.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], 8.0f);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], 15.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][0], 10.342469f);
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][1], 15.0f);
|
||||
|
||||
BKE_fcurve_keyframe_move_time_with_handles(&fcu->bezt[1], 47.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][0], 44.2671194f) << "Left handle time should be updated";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[0][1], 15.0f) << "Left handle should not move in value";
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], 47.0f) << "Frame time should have been updated";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], 15.0f) << "Frame should not move in value";
|
||||
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][0], 49.342469f) << "Right handle time should be updated";
|
||||
EXPECT_FLOAT_EQ(fcu->bezt[1].vec[2][1], 15.0f) << "Right handle should not move in value";
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, BKE_fcurve_calc_range)
|
||||
{
|
||||
FCurve *fcu = BKE_fcurve_create();
|
||||
|
@ -546,4 +579,127 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds)
|
|||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
static void set_key(FCurve *fcu, const int index, const float x, const float y)
|
||||
{
|
||||
fcu->bezt[index].vec[0][0] = x - 0.5f;
|
||||
fcu->bezt[index].vec[1][0] = x;
|
||||
fcu->bezt[index].vec[2][0] = x + 0.5f;
|
||||
|
||||
fcu->bezt[index].vec[0][1] = y;
|
||||
fcu->bezt[index].vec[1][1] = y;
|
||||
fcu->bezt[index].vec[2][1] = y;
|
||||
}
|
||||
|
||||
static FCurve *testcurve_with_duplicates()
|
||||
{
|
||||
/* Create a curve with some duplicate keys. The first ones are all with Y=1, the later repeats
|
||||
* increase Y-coordinates on every repeat. */
|
||||
FCurve *fcu = BKE_fcurve_create();
|
||||
ED_keyframes_add(fcu, 10); /* Avoid `insert_vert_fcurve`, that deduplicates the keys. */
|
||||
set_key(fcu, 0, 1.0f, 1.0f);
|
||||
set_key(fcu, 1, 327.16f, 1.0f);
|
||||
set_key(fcu, 2, 7.0f, 1.0f);
|
||||
set_key(fcu, 3, 47.0f, 1.0f);
|
||||
set_key(fcu, 4, 7.0f, 2.0f);
|
||||
set_key(fcu, 5, 47.0f, 2.0f);
|
||||
set_key(fcu, 6, 47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f);
|
||||
set_key(fcu, 7, 7.0f, 3.0f);
|
||||
set_key(fcu, 8, 3.0f, 1.0f);
|
||||
set_key(fcu, 9, 2.0f, 1.0f);
|
||||
return fcu;
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, sort_time_fcurve_stability)
|
||||
{
|
||||
FCurve *fcu = testcurve_with_duplicates();
|
||||
ASSERT_EQ(fcu->totvert, 10);
|
||||
|
||||
sort_time_fcurve(fcu);
|
||||
|
||||
/* The sorting should be stable, i.e. retain the original order when the
|
||||
* X-coordinates are identical. */
|
||||
ASSERT_EQ(fcu->totvert, 10) << "sorting should not influence number of keys";
|
||||
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(7.0f, 2.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[6].vec[1], float2(47.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[7].vec[1], float2(47.0f, 2.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[8].vec[1], float2(47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[9].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys)
|
||||
{
|
||||
FCurve *fcu = testcurve_with_duplicates();
|
||||
ASSERT_EQ(fcu->totvert, 10);
|
||||
sort_time_fcurve(fcu);
|
||||
|
||||
BKE_fcurve_deduplicate_keys(fcu);
|
||||
ASSERT_GE(fcu->totvert, 6); /* Protect against out-of-bounds access. */
|
||||
EXPECT_EQ(fcu->totvert, 6); /* The actual expected value. */
|
||||
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys_edge_cases)
|
||||
{
|
||||
FCurve *fcu = testcurve_with_duplicates();
|
||||
ASSERT_EQ(fcu->totvert, 10);
|
||||
|
||||
/* Update the 2nd and 2nd-to-last keys to test the edge cases. */
|
||||
set_key(fcu, 0, 1, 1);
|
||||
set_key(fcu, 1, 1, 2);
|
||||
set_key(fcu, 8, 327.16f, 1);
|
||||
set_key(fcu, 9, 327.16f, 2);
|
||||
|
||||
sort_time_fcurve(fcu);
|
||||
|
||||
BKE_fcurve_deduplicate_keys(fcu);
|
||||
ASSERT_EQ(fcu->totvert, 4);
|
||||
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 2.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(327.16f, 2.0f), 1e-3);
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
TEST(BKE_fcurve, BKE_fcurve_deduplicate_keys_prefer_whole_frames)
|
||||
{
|
||||
FCurve *fcu = testcurve_with_duplicates();
|
||||
ASSERT_EQ(fcu->totvert, 10);
|
||||
|
||||
/* Update the first key around 47.0 to be slightly before the frame. This gives us three keys on
|
||||
* 47-epsilon, 47, and 47+epsilon. The keys at index 5 and 6 already have this value, so the
|
||||
* `set_key` calls are unnecessary, but this way this test has a more local overview of the
|
||||
* situation under test. */
|
||||
set_key(fcu, 3, 47.0f - BEZT_BINARYSEARCH_THRESH, 1.0f);
|
||||
set_key(fcu, 5, 47.0f, 2.0f);
|
||||
set_key(fcu, 6, 47.0f + BEZT_BINARYSEARCH_THRESH, 3.0f);
|
||||
|
||||
sort_time_fcurve(fcu);
|
||||
|
||||
BKE_fcurve_deduplicate_keys(fcu);
|
||||
ASSERT_EQ(fcu->totvert, 6);
|
||||
EXPECT_V2_NEAR(fcu->bezt[0].vec[1], float2(1.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[1].vec[1], float2(2.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[2].vec[1], float2(3.0f, 1.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[3].vec[1], float2(7.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[4].vec[1], float2(47.0f, 3.0f), 1e-3);
|
||||
EXPECT_V2_NEAR(fcu->bezt[5].vec[1], float2(327.16f, 1.0f), 1e-3);
|
||||
|
||||
BKE_fcurve_free(fcu);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
||||
|
|
|
@ -1659,6 +1659,29 @@ int insert_keyframe(Main *bmain,
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ED_keyframes_add(FCurve *fcu, int num_keys_to_add)
|
||||
{
|
||||
BLI_assert_msg(num_keys_to_add >= 0, "cannot remove keyframes with this function");
|
||||
|
||||
if (num_keys_to_add == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fcu->bezt = MEM_recallocN(fcu->bezt, sizeof(BezTriple) * (fcu->totvert + num_keys_to_add));
|
||||
BezTriple *bezt = fcu->bezt + fcu->totvert; /* Pointer to the first new one. '*/
|
||||
|
||||
fcu->totvert += num_keys_to_add;
|
||||
|
||||
/* Iterate over the new keys to update their settings. */
|
||||
while (num_keys_to_add--) {
|
||||
/* Defaults, ignoring user-preference gives predictable results for API. */
|
||||
bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
|
||||
bezt->ipo = BEZT_IPO_BEZ;
|
||||
bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
|
||||
bezt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************** */
|
||||
/* KEYFRAME DELETION */
|
||||
|
||||
|
|
|
@ -123,6 +123,17 @@ int insert_vert_fcurve(struct FCurve *fcu,
|
|||
eBezTriple_KeyframeType keyframe_type,
|
||||
eInsertKeyFlags flag);
|
||||
|
||||
/**
|
||||
* Add the given number of keyframes to the FCurve. Their coordinates are
|
||||
* uninitialized, so the curve should not be used without further attention.
|
||||
*
|
||||
* The newly created keys are selected, existing keys are not touched.
|
||||
*
|
||||
* This can be used to allocate all the keys at once, and then update them
|
||||
* afterwards.
|
||||
*/
|
||||
void ED_keyframes_add(struct FCurve *fcu, int num_keys_to_add);
|
||||
|
||||
/* -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -692,6 +692,7 @@ static void rna_tag_animation_update(Main *bmain, ID *id)
|
|||
static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain)
|
||||
{
|
||||
sort_time_fcurve(fcu);
|
||||
/* TODO: Blender 4.0 should call BKE_fcurve_deduplicate_keys(fcu) here. */
|
||||
BKE_fcurve_handles_recalc(fcu);
|
||||
|
||||
rna_tag_animation_update(bmain, id);
|
||||
|
@ -1071,24 +1072,12 @@ static BezTriple *rna_FKeyframe_points_insert(
|
|||
|
||||
static void rna_FKeyframe_points_add(ID *id, FCurve *fcu, Main *bmain, int tot)
|
||||
{
|
||||
if (tot > 0) {
|
||||
BezTriple *bezt;
|
||||
|
||||
fcu->bezt = MEM_recallocN(fcu->bezt, sizeof(BezTriple) * (fcu->totvert + tot));
|
||||
|
||||
bezt = fcu->bezt + fcu->totvert;
|
||||
fcu->totvert += tot;
|
||||
|
||||
while (tot--) {
|
||||
/* Defaults, ignoring user-preference gives predictable results for API. */
|
||||
bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
|
||||
bezt->ipo = BEZT_IPO_BEZ;
|
||||
bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
|
||||
bezt++;
|
||||
}
|
||||
|
||||
rna_tag_animation_update(bmain, id);
|
||||
if (tot <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ED_keyframes_add(fcu, tot);
|
||||
rna_tag_animation_update(bmain, id);
|
||||
}
|
||||
|
||||
static void rna_FKeyframe_points_remove(
|
||||
|
@ -1118,6 +1107,24 @@ static void rna_FKeyframe_points_clear(ID *id, FCurve *fcu, Main *bmain)
|
|||
rna_tag_animation_update(bmain, id);
|
||||
}
|
||||
|
||||
static void rna_FKeyframe_points_sort(ID *id, FCurve *fcu, Main *bmain)
|
||||
{
|
||||
sort_time_fcurve(fcu);
|
||||
rna_tag_animation_update(bmain, id);
|
||||
}
|
||||
|
||||
static void rna_FKeyframe_points_deduplicate(ID *id, FCurve *fcu, Main *bmain)
|
||||
{
|
||||
BKE_fcurve_deduplicate_keys(fcu);
|
||||
rna_tag_animation_update(bmain, id);
|
||||
}
|
||||
|
||||
static void rna_FKeyframe_points_handles_recalc(ID *id, FCurve *fcu, Main *bmain)
|
||||
{
|
||||
BKE_fcurve_handles_recalc(fcu);
|
||||
rna_tag_animation_update(bmain, id);
|
||||
}
|
||||
|
||||
static FCM_EnvelopeData *rna_FModifierEnvelope_points_add(
|
||||
ID *id, FModifier *fmod, Main *bmain, ReportList *reports, float frame)
|
||||
{
|
||||
|
@ -2414,6 +2421,22 @@ static void rna_def_fcurve_keyframe_points(BlenderRNA *brna, PropertyRNA *cprop)
|
|||
func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear");
|
||||
RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||
|
||||
func = RNA_def_function(srna, "sort", "rna_FKeyframe_points_sort");
|
||||
RNA_def_function_ui_description(func, "Ensure all keyframe points are chronologically sorted");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||
|
||||
func = RNA_def_function(srna, "deduplicate", "rna_FKeyframe_points_deduplicate");
|
||||
RNA_def_function_ui_description(
|
||||
func,
|
||||
"Ensure there are no duplicate keys. Assumes that the points have already been sorted");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||
|
||||
func = RNA_def_function(srna, "handles_recalc", "rna_FKeyframe_points_handles_recalc");
|
||||
RNA_def_function_ui_description(func,
|
||||
"Update handles after modifications to the keyframe points, to "
|
||||
"update things like auto-clamping");
|
||||
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
|
||||
}
|
||||
|
||||
static void rna_def_fcurve(BlenderRNA *brna)
|
||||
|
|
|
@ -42,23 +42,6 @@ using fn::ValueOrField;
|
|||
using geo_eval_log::NamedAttributeUsage;
|
||||
using geo_eval_log::NodeWarningType;
|
||||
|
||||
/**
|
||||
* An anonymous attribute created by a node.
|
||||
*/
|
||||
class NodeAnonymousAttributeID : public AnonymousAttributeID {
|
||||
std::string long_name_;
|
||||
std::string socket_name_;
|
||||
|
||||
public:
|
||||
NodeAnonymousAttributeID(const Object &object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &bnode,
|
||||
const StringRef identifier,
|
||||
const StringRef name);
|
||||
|
||||
std::string user_name() const override;
|
||||
};
|
||||
|
||||
class GeoNodeExecParams {
|
||||
private:
|
||||
const bNode &node_;
|
||||
|
@ -66,18 +49,22 @@ class GeoNodeExecParams {
|
|||
const lf::Context &lf_context_;
|
||||
const Span<int> lf_input_for_output_bsocket_usage_;
|
||||
const Span<int> lf_input_for_attribute_propagation_to_output_;
|
||||
const FunctionRef<AnonymousAttributeIDPtr(int)> get_output_attribute_id_;
|
||||
|
||||
public:
|
||||
GeoNodeExecParams(const bNode &node,
|
||||
lf::Params ¶ms,
|
||||
const lf::Context &lf_context,
|
||||
const Span<int> lf_input_for_output_bsocket_usage,
|
||||
const Span<int> lf_input_for_attribute_propagation_to_output)
|
||||
const Span<int> lf_input_for_attribute_propagation_to_output,
|
||||
const FunctionRef<AnonymousAttributeIDPtr(int)> get_output_attribute_id)
|
||||
: node_(node),
|
||||
params_(params),
|
||||
lf_context_(lf_context),
|
||||
lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage),
|
||||
lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output)
|
||||
lf_input_for_attribute_propagation_to_output_(
|
||||
lf_input_for_attribute_propagation_to_output),
|
||||
get_output_attribute_id_(get_output_attribute_id)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -240,8 +227,6 @@ class GeoNodeExecParams {
|
|||
*/
|
||||
void error_message_add(const NodeWarningType type, StringRef message) const;
|
||||
|
||||
std::string attribute_producer_name() const;
|
||||
|
||||
void set_default_remaining_outputs();
|
||||
|
||||
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
|
||||
|
@ -268,14 +253,7 @@ class GeoNodeExecParams {
|
|||
return {};
|
||||
}
|
||||
const bNodeSocket &output_socket = node_.output_by_identifier(output_identifier);
|
||||
const GeoNodesLFUserData &user_data = *this->user_data();
|
||||
const ComputeContext &compute_context = *user_data.compute_context;
|
||||
return MEM_new<NodeAnonymousAttributeID>(__func__,
|
||||
*user_data.modifier_data->self_object,
|
||||
compute_context,
|
||||
node_,
|
||||
output_identifier,
|
||||
output_socket.name);
|
||||
return get_output_attribute_id_(output_socket.index());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -322,7 +322,9 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
|||
/**
|
||||
* For inputs this means that the input field is evaluated on all geometry inputs. For outputs
|
||||
* it means that this contains an anonymous attribute reference that is available on all geometry
|
||||
* outputs.
|
||||
* outputs. This sockets value does not have to be output manually in the node. It's done
|
||||
* automatically by #LazyFunctionForGeometryNode. This allows outputting this field even if the
|
||||
* geometry output does not have to be computed.
|
||||
*/
|
||||
Self &field_on_all()
|
||||
{
|
||||
|
|
|
@ -174,8 +174,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
break;
|
||||
}
|
||||
|
||||
const CPPType &type = field.cpp_type();
|
||||
|
||||
/* Run on the instances component separately to only affect the top level of instances. */
|
||||
if (domain == ATTR_DOMAIN_INSTANCE) {
|
||||
if (geometry_set.has_instances()) {
|
||||
|
@ -198,34 +196,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
});
|
||||
}
|
||||
|
||||
GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>(
|
||||
std::move(attribute_id), type, params.attribute_producer_name())};
|
||||
|
||||
switch (data_type) {
|
||||
case CD_PROP_FLOAT: {
|
||||
params.set_output(output_identifier, Field<float>(output_field));
|
||||
break;
|
||||
}
|
||||
case CD_PROP_FLOAT3: {
|
||||
params.set_output(output_identifier, Field<float3>(output_field));
|
||||
break;
|
||||
}
|
||||
case CD_PROP_COLOR: {
|
||||
params.set_output(output_identifier, Field<ColorGeometry4f>(output_field));
|
||||
break;
|
||||
}
|
||||
case CD_PROP_BOOL: {
|
||||
params.set_output(output_identifier, Field<bool>(output_field));
|
||||
break;
|
||||
}
|
||||
case CD_PROP_INT32: {
|
||||
params.set_output(output_identifier, Field<int>(output_field));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
params.set_output("Geometry", geometry_set);
|
||||
}
|
||||
|
||||
|
|
|
@ -160,11 +160,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
selection.span[i] = true;
|
||||
}
|
||||
selection.finish();
|
||||
|
||||
params.set_output(
|
||||
"Intersecting Edges",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.intersecting_edges_id), params.attribute_producer_name()));
|
||||
}
|
||||
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(result));
|
||||
|
|
|
@ -81,9 +81,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
if (AnonymousAttributeIDPtr outer_points_id = params.get_output_anonymous_attribute_id_if_needed(
|
||||
"Outer Points")) {
|
||||
create_selection_output(output.get_component_for_write<CurveComponent>(), outer_points_id);
|
||||
params.set_output("Outer Points",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(outer_points_id), params.attribute_producer_name()));
|
||||
}
|
||||
params.set_output("Curve", std::move(output));
|
||||
}
|
||||
|
|
|
@ -200,21 +200,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
}
|
||||
|
||||
params.set_output("Points", std::move(geometry_set));
|
||||
if (tangent_anonymous_id) {
|
||||
params.set_output("Tangent",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(tangent_anonymous_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (normal_anonymous_id) {
|
||||
params.set_output("Normal",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(normal_anonymous_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (rotation_anonymous_id) {
|
||||
params.set_output("Rotation",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(rotation_anonymous_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_curve_to_points_cc
|
||||
|
|
|
@ -590,19 +590,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
});
|
||||
|
||||
params.set_output("Points", std::move(geometry_set));
|
||||
|
||||
if (attribute_outputs.normal_id) {
|
||||
params.set_output(
|
||||
"Normal",
|
||||
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.rotation_id) {
|
||||
params.set_output(
|
||||
"Rotation",
|
||||
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_distribute_points_on_faces_cc
|
||||
|
|
|
@ -1098,12 +1098,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
return;
|
||||
}
|
||||
|
||||
if (attribute_outputs.duplicate_index) {
|
||||
params.set_output(
|
||||
"Duplicate Index",
|
||||
AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.duplicate_index),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
|
||||
|
|
|
@ -1376,16 +1376,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
});
|
||||
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
if (attribute_outputs.top_id) {
|
||||
params.set_output("Top",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.side_id) {
|
||||
params.set_output("Side",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_extrude_mesh_cc
|
||||
|
|
|
@ -836,16 +836,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
}
|
||||
|
||||
params.set_output("Curves", std::move(new_curves));
|
||||
if (index_attribute_id) {
|
||||
params.set_output("Closest Index",
|
||||
AnonymousAttributeFieldInput::Create<int>(std::move(index_attribute_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
if (weight_attribute_id) {
|
||||
params.set_output("Closest Weight",
|
||||
AnonymousAttributeFieldInput::Create<float>(
|
||||
std::move(weight_attribute_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_interpolate_curves_cc
|
||||
|
|
|
@ -851,29 +851,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
/* Transform the mesh so that the base of the cone is at the origin. */
|
||||
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
|
||||
|
||||
if (attribute_outputs.top_id) {
|
||||
params.set_output("Top",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.bottom_id) {
|
||||
params.set_output(
|
||||
"Bottom",
|
||||
AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.side_id) {
|
||||
params.set_output("Side",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.uv_map_id) {
|
||||
params.set_output(
|
||||
"UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.uv_map_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
}
|
||||
|
||||
|
|
|
@ -112,12 +112,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z, uv_map_id.get());
|
||||
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
|
||||
if (uv_map_id) {
|
||||
params.set_output("UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(uv_map_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_mesh_primitive_cube_cc
|
||||
|
|
|
@ -122,29 +122,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
fill,
|
||||
attribute_outputs);
|
||||
|
||||
if (attribute_outputs.top_id) {
|
||||
params.set_output("Top",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.top_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.bottom_id) {
|
||||
params.set_output(
|
||||
"Bottom",
|
||||
AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.side_id) {
|
||||
params.set_output("Side",
|
||||
AnonymousAttributeFieldInput::Create<bool>(
|
||||
std::move(attribute_outputs.side_id), params.attribute_producer_name()));
|
||||
}
|
||||
if (attribute_outputs.uv_map_id) {
|
||||
params.set_output(
|
||||
"UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.uv_map_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
}
|
||||
|
||||
|
|
|
@ -205,12 +205,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
BKE_id_material_eval_ensure_default_slot(&mesh->id);
|
||||
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
|
||||
if (uv_map_id) {
|
||||
params.set_output("UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(uv_map_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_mesh_primitive_grid_cc
|
||||
|
|
|
@ -115,12 +115,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
|
||||
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius, uv_map_id.get());
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
|
||||
if (uv_map_id) {
|
||||
params.set_output("UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(uv_map_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc
|
||||
|
|
|
@ -363,11 +363,6 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
|
||||
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num, uv_map_id.get());
|
||||
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
|
||||
if (uv_map_id) {
|
||||
params.set_output("UV Map",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(uv_map_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc
|
||||
|
|
|
@ -348,9 +348,6 @@ static void create_attributes(GeoNodeExecParams ¶ms,
|
|||
*line_id, ATTR_DOMAIN_INSTANCE);
|
||||
line_attribute.span.copy_from(layout.line_numbers);
|
||||
line_attribute.finish();
|
||||
params.set_output("Line",
|
||||
AnonymousAttributeFieldInput::Create<int>(std::move(line_id),
|
||||
params.attribute_producer_name()));
|
||||
}
|
||||
|
||||
if (AnonymousAttributeIDPtr pivot_id = params.get_output_anonymous_attribute_id_if_needed(
|
||||
|
@ -363,9 +360,6 @@ static void create_attributes(GeoNodeExecParams ¶ms,
|
|||
}
|
||||
|
||||
pivot_attribute.finish();
|
||||
params.set_output("Pivot Point",
|
||||
AnonymousAttributeFieldInput::Create<float3>(
|
||||
std::move(pivot_id), params.attribute_producer_name()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "BLI_cpp_types.hh"
|
||||
#include "BLI_dot_export.hh"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_hash_md5.h"
|
||||
#include "BLI_lazy_threading.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
|
@ -102,6 +103,43 @@ static void lazy_function_interface_from_node(const bNode &node,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An anonymous attribute created by a node.
|
||||
*/
|
||||
class NodeAnonymousAttributeID : public AnonymousAttributeID {
|
||||
std::string long_name_;
|
||||
std::string socket_name_;
|
||||
|
||||
public:
|
||||
NodeAnonymousAttributeID(const Object &object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &bnode,
|
||||
const StringRef identifier,
|
||||
const StringRef name)
|
||||
: socket_name_(name)
|
||||
{
|
||||
const ComputeContextHash &hash = compute_context.hash();
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
|
||||
long_name_ = ss.str();
|
||||
}
|
||||
{
|
||||
uint64_t hash_result[2];
|
||||
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
|
||||
std::stringstream ss;
|
||||
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
|
||||
name_ = ss.str();
|
||||
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
std::string user_name() const override
|
||||
{
|
||||
return socket_name_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for most normal geometry nodes like Subdivision Surface and Set Position.
|
||||
*/
|
||||
|
@ -117,6 +155,25 @@ class LazyFunctionForGeometryNode : public LazyFunction {
|
|||
* propagated to the output.
|
||||
*/
|
||||
const Span<int> lf_input_for_attribute_propagation_to_output_;
|
||||
/**
|
||||
* Maps #bNodeSocket::index_in_tree to input/output indices of the current lazy-function.
|
||||
*/
|
||||
const Span<int> lf_index_by_bsocket_;
|
||||
/**
|
||||
* A bool for every output bsocket. If true, the socket just outputs a field containing an
|
||||
* anonymous attribute id. If only such outputs are requested by other nodes, the node itself
|
||||
* does not have to execute.
|
||||
*/
|
||||
Vector<bool> is_attribute_output_bsocket_;
|
||||
|
||||
struct OutputAttributeID {
|
||||
int bsocket_index;
|
||||
AnonymousAttributeIDPtr attribute_id;
|
||||
};
|
||||
|
||||
struct Storage {
|
||||
Vector<OutputAttributeID, 1> attributes;
|
||||
};
|
||||
|
||||
public:
|
||||
LazyFunctionForGeometryNode(const bNode &node,
|
||||
|
@ -126,7 +183,9 @@ class LazyFunctionForGeometryNode : public LazyFunction {
|
|||
: node_(node),
|
||||
lf_input_for_output_bsocket_usage_(r_lf_input_for_output_bsocket_usage),
|
||||
lf_input_for_attribute_propagation_to_output_(
|
||||
r_lf_input_for_attribute_propagation_to_output)
|
||||
r_lf_input_for_attribute_propagation_to_output),
|
||||
lf_index_by_bsocket_(r_lf_index_by_bsocket),
|
||||
is_attribute_output_bsocket_(node.output_sockets().size(), false)
|
||||
{
|
||||
BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
|
||||
debug_name_ = node.name;
|
||||
|
@ -137,6 +196,16 @@ class LazyFunctionForGeometryNode : public LazyFunction {
|
|||
if (relations == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!relations->available_relations.is_empty()) {
|
||||
/* Inputs are only used when an output is used that is not just outputting an anonymous
|
||||
* attribute field. */
|
||||
for (lf::Input &input : inputs_) {
|
||||
input.usage = lf::ValueUsage::Maybe;
|
||||
}
|
||||
for (const aal::AvailableRelation &relation : relations->available_relations) {
|
||||
is_attribute_output_bsocket_[relation.field_output] = true;
|
||||
}
|
||||
}
|
||||
Vector<const bNodeSocket *> handled_field_outputs;
|
||||
for (const aal::AvailableRelation &relation : relations->available_relations) {
|
||||
const bNodeSocket &output_bsocket = node.output_socket(relation.field_output);
|
||||
|
@ -160,16 +229,90 @@ class LazyFunctionForGeometryNode : public LazyFunction {
|
|||
}
|
||||
}
|
||||
|
||||
void *init_storage(LinearAllocator<> &allocator) const override
|
||||
{
|
||||
return allocator.construct<Storage>().release();
|
||||
}
|
||||
|
||||
void destruct_storage(void *storage) const override
|
||||
{
|
||||
Storage *s = static_cast<Storage *>(storage);
|
||||
std::destroy_at(s);
|
||||
}
|
||||
|
||||
void execute_impl(lf::Params ¶ms, const lf::Context &context) const override
|
||||
{
|
||||
Storage *storage = static_cast<Storage *>(context.storage);
|
||||
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
|
||||
BLI_assert(user_data != nullptr);
|
||||
|
||||
/* Lazily create the required anonymous attribute ids. */
|
||||
auto get_output_attribute_id = [&](const int output_bsocket_index) -> AnonymousAttributeIDPtr {
|
||||
for (const OutputAttributeID &node_output_attribute : storage->attributes) {
|
||||
if (node_output_attribute.bsocket_index == output_bsocket_index) {
|
||||
return node_output_attribute.attribute_id;
|
||||
}
|
||||
}
|
||||
const bNodeSocket &bsocket = node_.output_socket(output_bsocket_index);
|
||||
AnonymousAttributeIDPtr attribute_id = MEM_new<NodeAnonymousAttributeID>(
|
||||
__func__,
|
||||
*user_data->modifier_data->self_object,
|
||||
*user_data->compute_context,
|
||||
node_,
|
||||
bsocket.identifier,
|
||||
bsocket.name);
|
||||
storage->attributes.append({output_bsocket_index, attribute_id});
|
||||
return attribute_id;
|
||||
};
|
||||
|
||||
bool used_non_attribute_output_exists = false;
|
||||
for (const int output_bsocket_index : node_.output_sockets().index_range()) {
|
||||
const bNodeSocket &output_bsocket = node_.output_socket(output_bsocket_index);
|
||||
const int lf_index = lf_index_by_bsocket_[output_bsocket.index_in_tree()];
|
||||
if (lf_index == -1) {
|
||||
continue;
|
||||
}
|
||||
const lf::ValueUsage output_usage = params.get_output_usage(lf_index);
|
||||
if (output_usage == lf::ValueUsage::Unused) {
|
||||
continue;
|
||||
}
|
||||
if (is_attribute_output_bsocket_[output_bsocket_index]) {
|
||||
if (params.output_was_set(lf_index)) {
|
||||
continue;
|
||||
}
|
||||
this->output_anonymous_attribute_field(
|
||||
params, lf_index, get_output_attribute_id(output_bsocket_index));
|
||||
}
|
||||
else {
|
||||
if (output_usage == lf::ValueUsage::Used) {
|
||||
used_non_attribute_output_exists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!used_non_attribute_output_exists) {
|
||||
/* Only attribute outputs are used currently, no need to evaluate the full node and its
|
||||
* inputs. */
|
||||
return;
|
||||
}
|
||||
|
||||
bool missing_input = false;
|
||||
for (const int lf_index : inputs_.index_range()) {
|
||||
if (params.try_get_input_data_ptr_or_request(lf_index) == nullptr) {
|
||||
missing_input = true;
|
||||
}
|
||||
}
|
||||
if (missing_input) {
|
||||
/* Wait until all inputs are available. */
|
||||
return;
|
||||
}
|
||||
|
||||
GeoNodeExecParams geo_params{node_,
|
||||
params,
|
||||
context,
|
||||
lf_input_for_output_bsocket_usage_,
|
||||
lf_input_for_attribute_propagation_to_output_};
|
||||
lf_input_for_attribute_propagation_to_output_,
|
||||
get_output_attribute_id};
|
||||
|
||||
geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now();
|
||||
node_.typeinfo->geometry_node_execute(geo_params);
|
||||
|
@ -182,6 +325,24 @@ class LazyFunctionForGeometryNode : public LazyFunction {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the given anonymous attribute id as a field.
|
||||
*/
|
||||
void output_anonymous_attribute_field(lf::Params ¶ms,
|
||||
const int lf_index,
|
||||
AnonymousAttributeIDPtr attribute_id) const
|
||||
{
|
||||
const ValueOrFieldCPPType &value_or_field_cpp_type = *ValueOrFieldCPPType::get_from_self(
|
||||
*outputs_[lf_index].type);
|
||||
GField output_field{
|
||||
std::make_shared<AnonymousAttributeFieldInput>(std::move(attribute_id),
|
||||
value_or_field_cpp_type.value,
|
||||
node_.label_or_name() + TIP_(" node"))};
|
||||
void *r_value = params.get_output_data_ptr(lf_index);
|
||||
value_or_field_cpp_type.construct_from_field(r_value, std::move(output_field));
|
||||
params.output_set(lf_index);
|
||||
}
|
||||
|
||||
std::string input_name(const int index) const override
|
||||
{
|
||||
for (const bNodeSocket *bsocket : node_.output_sockets()) {
|
||||
|
|
|
@ -9,40 +9,10 @@
|
|||
|
||||
#include "NOD_geometry_exec.hh"
|
||||
|
||||
#include "BLI_hash_md5.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
NodeAnonymousAttributeID::NodeAnonymousAttributeID(const Object &object,
|
||||
const ComputeContext &compute_context,
|
||||
const bNode &bnode,
|
||||
const StringRef identifier,
|
||||
const StringRef name)
|
||||
: socket_name_(name)
|
||||
{
|
||||
const ComputeContextHash &hash = compute_context.hash();
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
|
||||
long_name_ = ss.str();
|
||||
}
|
||||
{
|
||||
uint64_t hash_result[2];
|
||||
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
|
||||
std::stringstream ss;
|
||||
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
|
||||
name_ = ss.str();
|
||||
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
std::string NodeAnonymousAttributeID::user_name() const
|
||||
{
|
||||
return socket_name_;
|
||||
}
|
||||
|
||||
void GeoNodeExecParams::error_message_add(const NodeWarningType type,
|
||||
const StringRef message) const
|
||||
{
|
||||
|
@ -153,11 +123,6 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::string GeoNodeExecParams::attribute_producer_name() const
|
||||
{
|
||||
return node_.label_or_name() + TIP_(" node");
|
||||
}
|
||||
|
||||
void GeoNodeExecParams::set_default_remaining_outputs()
|
||||
{
|
||||
params_.set_default_remaining_outputs();
|
||||
|
|
|
@ -75,7 +75,7 @@ static PyObject *bpy_rna_region_as_string(PyObject *self, PyObject *args, PyObje
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (PyDict_GET_SIZE(kwds) > 0) {
|
||||
if (kwds && PyDict_GET_SIZE(kwds) > 0) {
|
||||
txt_sel_set(text, region.curl, region.curc, region.sell, region.selc);
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ static PyObject *bpy_rna_region_from_string(PyObject *self, PyObject *args, PyOb
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (PyDict_GET_SIZE(kwds) > 0) {
|
||||
if (kwds && PyDict_GET_SIZE(kwds) > 0) {
|
||||
txt_sel_set(text, region.curl, region.curc, region.sell, region.selc);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue