Geometry Nodes: add simulation support #104924
|
@ -692,10 +692,12 @@ if(WITH_GHOST_WAYLAND)
|
|||
|
||||
if(WITH_GHOST_WAYLAND_LIBDECOR)
|
||||
if(_use_system_wayland)
|
||||
pkg_check_modules(libdecor REQUIRED libdecor-0>=0.1)
|
||||
pkg_check_modules(libdecor libdecor-0>=0.1)
|
||||
else()
|
||||
set(libdecor_INCLUDE_DIRS "${LIBDIR}/wayland_libdecor/include/libdecor-0")
|
||||
set(libdecor_FOUND ON)
|
||||
endif()
|
||||
set_and_warn_library_found("libdecor" libdecor_FOUND WITH_GHOST_WAYLAND_LIBDECOR)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_WAYLAND_DBUS)
|
||||
|
|
|
@ -224,10 +224,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
|
|||
}
|
||||
|
||||
int blas_index = metal_ancillaries->blas_userID_to_index_lookUp[local_object];
|
||||
// transform the ray into object's local space
|
||||
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
|
||||
r.origin = transform_point(&itfm, r.origin);
|
||||
r.direction = transform_direction(&itfm, r.direction);
|
||||
|
||||
if (!(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED)) {
|
||||
// transform the ray into object's local space
|
||||
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
|
||||
r.origin = transform_point(&itfm, r.origin);
|
||||
r.direction = transform_direction(&itfm, r.direction);
|
||||
}
|
||||
|
||||
intersection = metalrt_intersect.intersect(
|
||||
r,
|
||||
|
|
|
@ -761,7 +761,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet
|
|||
#ifdef WITH_VULKAN_BACKEND
|
||||
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 0, debug_context);
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 2, debug_context);
|
||||
if (!context->initializeDrawingContext()) {
|
||||
delete context;
|
||||
return NULL;
|
||||
|
|
|
@ -6298,7 +6298,7 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glS
|
|||
wl_surface,
|
||||
display_->wl_display,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
|
|
|
@ -267,7 +267,7 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet
|
|||
#ifdef WITH_VULKAN_BACKEND
|
||||
/* Vulkan does not need a window. */
|
||||
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
context = new GHOST_ContextVK(false, (HWND)0, 1, 0, debug_context);
|
||||
context = new GHOST_ContextVK(false, (HWND)0, 1, 2, debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
delete context;
|
||||
|
|
|
@ -417,7 +417,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti
|
|||
#ifdef WITH_VULKAN_BACKEND
|
||||
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
|
||||
context = new GHOST_ContextVK(
|
||||
false, GHOST_kVulkanPlatformX11, 0, m_display, NULL, NULL, 1, 0, debug_context);
|
||||
false, GHOST_kVulkanPlatformX11, 0, m_display, NULL, NULL, 1, 2, debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
delete context;
|
||||
|
|
|
@ -815,7 +815,7 @@ GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType ty
|
|||
{
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
if (type == GHOST_kDrawingContextTypeVulkan) {
|
||||
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 0, true);
|
||||
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 2, true);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
delete context;
|
||||
|
|
|
@ -1744,7 +1744,7 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
|
|||
window_->wl_surface,
|
||||
system_->wl_display(),
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
true);
|
||||
break;
|
||||
#endif
|
||||
|
|
|
@ -637,7 +637,7 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
|
|||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
else if (type == GHOST_kDrawingContextTypeVulkan) {
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 0, m_debug_context);
|
||||
GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 2, m_debug_context);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
|
|
|
@ -1235,7 +1235,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
|
|||
NULL,
|
||||
NULL,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
m_is_debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -47,6 +47,8 @@ def get_context_modifier(context):
|
|||
if context.area.type == 'PROPERTIES':
|
||||
modifier = context.modifier
|
||||
else:
|
||||
if context.object is None:
|
||||
return False
|
||||
modifier = context.object.modifiers.active
|
||||
if modifier is None or modifier.type != 'NODES':
|
||||
return None
|
||||
|
|
|
@ -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 -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_userdef_enums.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -448,7 +450,7 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
|
|||
*/
|
||||
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
|
||||
struct ID *id,
|
||||
uint duplicate_flags,
|
||||
eDupli_ID_Flags duplicate_flags,
|
||||
int copy_flags);
|
||||
|
||||
/**
|
||||
|
|
|
@ -148,6 +148,28 @@ enum {
|
|||
IDWALK_INCLUDE_UI = (1 << 2),
|
||||
/** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */
|
||||
IDWALK_IGNORE_EMBEDDED_ID = (1 << 3),
|
||||
/**
|
||||
* Do not access original processed pointer's data, only process its address value.
|
||||
*
|
||||
* This is required in cases where to current address may not be valid anymore (e.g. during
|
||||
* readfile process). A few ID pointers (like e.g. the `LayerCollection.collection` one) are by
|
||||
* default accessed to check things (e.g. whether they are pointing to an embedded ID or a
|
||||
* regular one).
|
||||
*
|
||||
* \note Access to owning embedded ID pointers (e.g. `Scene.master_collection`) is not affected
|
||||
* here, these are presumed always valid.
|
||||
*
|
||||
* \note This flag is mutually exclusive with `IDWALK_READONLY` and `IDWALK_RECURSE`, since by
|
||||
* definition the only thing doable in readonly case is accessing current ID pointer, and this is
|
||||
* also required for recursion.
|
||||
*
|
||||
* \note After remapping, code may access the newly set ID pointer, which is always presumed
|
||||
* valid.
|
||||
*
|
||||
* \warning Use only with great caution, this flag will modify the handling of some ID pointers
|
||||
* (especially when it comes to detecting `IDWALK_CB_EMBEDDED_NOT_OWNING` usages).
|
||||
*/
|
||||
IDWALK_NO_ORIG_POINTERS_ACCESS = (1 << 5),
|
||||
|
||||
/**
|
||||
* Also process internal ID pointers like `ID.newid` or `ID.orig_id`.
|
||||
|
|
|
@ -88,6 +88,13 @@ enum {
|
|||
/** Do NOT tag IDs which had some of their ID pointers updated for update in the depsgraph, or ID
|
||||
* type specific updates, like e.g. with node trees. */
|
||||
ID_REMAP_SKIP_UPDATE_TAGGING = 1 << 19,
|
||||
/**
|
||||
* Do not attempt to access original ID pointers (triggers usages of
|
||||
* `IDWALK_NO_ORIG_POINTERS_ACCESS` too).
|
||||
*
|
||||
* Use when original ID pointers values are (probably) not valid, e.g. dureing readfile process.
|
||||
*/
|
||||
ID_REMAP_NO_ORIG_POINTERS_ACCESS = 1 << 20,
|
||||
};
|
||||
|
||||
typedef enum eIDRemapType {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "DNA_object_enums.h"
|
||||
#include "DNA_userdef_enums.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -234,7 +235,7 @@ bool BKE_object_obdata_is_libdata(const struct Object *ob);
|
|||
*/
|
||||
struct Object *BKE_object_duplicate(struct Main *bmain,
|
||||
struct Object *ob,
|
||||
uint dupflag,
|
||||
eDupli_ID_Flags dupflag,
|
||||
uint duplicate_options);
|
||||
|
||||
/**
|
||||
|
|
|
@ -168,6 +168,7 @@ static void collection_free_data(ID *id)
|
|||
static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
{
|
||||
Collection *collection = (Collection *)id;
|
||||
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_ID(
|
||||
data, collection->runtime.owner_id, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
|
||||
|
@ -197,6 +198,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
|
|||
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad
|
||||
* anyway... */
|
||||
const int cb_flag = ((parent->collection != NULL &&
|
||||
(data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0 &&
|
||||
(parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
|
||||
IDWALK_CB_EMBEDDED_NOT_OWNING :
|
||||
IDWALK_CB_NOP);
|
||||
|
@ -1467,11 +1469,16 @@ bool BKE_collection_object_replace(Main *bmain,
|
|||
return false;
|
||||
}
|
||||
|
||||
id_us_min(&cob->ob->id);
|
||||
cob->ob = ob_new;
|
||||
id_us_plus(&cob->ob->id);
|
||||
if (!BLI_ghash_haskey(collection->runtime.gobject_hash, ob_new)) {
|
||||
id_us_min(&cob->ob->id);
|
||||
cob->ob = ob_new;
|
||||
id_us_plus(&cob->ob->id);
|
||||
|
||||
BLI_ghash_insert(collection->runtime.gobject_hash, cob->ob, cob);
|
||||
BLI_ghash_insert(collection->runtime.gobject_hash, cob->ob, cob);
|
||||
}
|
||||
else {
|
||||
collection_object_remove_no_gobject_hash(bmain, collection, cob, false);
|
||||
}
|
||||
|
||||
if (BKE_collection_is_in_scene(collection)) {
|
||||
BKE_main_collection_sync(bmain);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -874,7 +874,12 @@ static void id_embedded_swap(ID **embedded_id_a,
|
|||
struct IDRemapper *remapper_id_b)
|
||||
{
|
||||
if (embedded_id_a != NULL && *embedded_id_a != NULL) {
|
||||
BLI_assert(embedded_id_b != NULL && *embedded_id_b != NULL);
|
||||
BLI_assert(embedded_id_b != NULL);
|
||||
|
||||
if (*embedded_id_b == NULL) {
|
||||
/* Cannot swap anything if one of the embedded IDs is NULL. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not remap internal references to itself here, since embedded IDs pointers also need to be
|
||||
* potentially remapped in owner ID's data, which will also handle embedded IDs data. */
|
||||
|
|
|
@ -201,6 +201,12 @@ static bool library_foreach_ID_link(Main *bmain,
|
|||
LibraryForeachIDData data = {.bmain = bmain};
|
||||
|
||||
BLI_assert(inherit_data == NULL || data.bmain == inherit_data->bmain);
|
||||
/* `IDWALK_NO_ORIG_POINTERS_ACCESS` is mutually exclusive with both `IDWALK_READONLY` and
|
||||
* `IDWALK_RECURSE`. */
|
||||
BLI_assert((flag & (IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_READONLY)) !=
|
||||
(IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_READONLY));
|
||||
BLI_assert((flag & (IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_RECURSE)) !=
|
||||
(IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_RECURSE));
|
||||
|
||||
if (flag & IDWALK_RECURSE) {
|
||||
/* For now, recursion implies read-only, and no internal pointers. */
|
||||
|
|
|
@ -483,7 +483,9 @@ static void libblock_remap_data(Main *bmain,
|
|||
(((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
|
||||
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
|
||||
IDWALK_NOP) |
|
||||
((remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0 ? IDWALK_INCLUDE_UI : IDWALK_NOP));
|
||||
((remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0 ? IDWALK_INCLUDE_UI : IDWALK_NOP) |
|
||||
((remap_flags & ID_REMAP_NO_ORIG_POINTERS_ACCESS) != 0 ? IDWALK_NO_ORIG_POINTERS_ACCESS :
|
||||
IDWALK_NOP));
|
||||
|
||||
id_remap_data.id_remapper = id_remapper;
|
||||
id_remap_data.type = remap_type;
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_multires.h"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_eval.h"
|
||||
|
@ -135,8 +135,8 @@ struct MultiresReshapeSmoothContext {
|
|||
* The data is actually stored as a delta, which is then to be added to the higher levels. */
|
||||
LinearGrids linear_delta_grids;
|
||||
|
||||
/* Index i of this map indicates that base edge i is adjacent to at least one face. */
|
||||
BLI_bitmap *non_loose_base_edge_map;
|
||||
/* From #Mesh::loose_edges(). May be empty. */
|
||||
blender::BitSpan loose_base_edges;
|
||||
|
||||
/* Subdivision surface created for geometry at a reshape level. */
|
||||
Subdiv *reshape_subdiv;
|
||||
|
@ -488,9 +488,9 @@ static float get_effective_crease(const MultiresReshapeSmoothContext *reshape_sm
|
|||
const int base_edge_index)
|
||||
{
|
||||
if (!is_crease_supported(reshape_smooth_context)) {
|
||||
return 255;
|
||||
return 1.0f;
|
||||
}
|
||||
const float *creases = reshape_smooth_context->reshape_context->cd_vertex_crease;
|
||||
const float *creases = reshape_smooth_context->reshape_context->cd_edge_crease;
|
||||
return creases ? creases[base_edge_index] : 0.0f;
|
||||
}
|
||||
|
||||
|
@ -524,7 +524,7 @@ static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
|
|||
|
||||
linear_grids_init(&reshape_smooth_context->linear_delta_grids);
|
||||
|
||||
reshape_smooth_context->non_loose_base_edge_map = nullptr;
|
||||
reshape_smooth_context->loose_base_edges = {};
|
||||
reshape_smooth_context->reshape_subdiv = nullptr;
|
||||
reshape_smooth_context->base_surface_grids = nullptr;
|
||||
|
||||
|
@ -556,8 +556,6 @@ static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_con
|
|||
|
||||
static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context)
|
||||
{
|
||||
MEM_freeN(reshape_smooth_context->non_loose_base_edge_map);
|
||||
|
||||
context_free_geometry(reshape_smooth_context);
|
||||
context_free_subdiv(reshape_smooth_context);
|
||||
base_surface_grids_free(reshape_smooth_context);
|
||||
|
@ -619,13 +617,11 @@ static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
|
|||
|
||||
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
|
||||
const float *cd_vertex_crease = reshape_context->cd_vertex_crease;
|
||||
|
||||
if (cd_vertex_crease == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
float crease = cd_vertex_crease[coarse_vertex_index];
|
||||
|
||||
if (crease == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
@ -833,7 +829,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
|
|||
return;
|
||||
}
|
||||
/* Ignore all loose edges as well, as they are not communicated to the OpenSubdiv. */
|
||||
if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, coarse_edge_index)) {
|
||||
if (!reshape_smooth_context->loose_base_edges.is_empty() &&
|
||||
reshape_smooth_context->loose_base_edges[coarse_edge_index]) {
|
||||
return;
|
||||
}
|
||||
/* Edges without crease are to be ignored as well. */
|
||||
|
@ -848,24 +845,20 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap
|
|||
{
|
||||
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
|
||||
const Mesh *base_mesh = reshape_context->base_mesh;
|
||||
const blender::OffsetIndices base_polys = reshape_context->base_polys;
|
||||
const blender::Span<int> base_corner_edges = reshape_context->base_corner_edges;
|
||||
|
||||
reshape_smooth_context->non_loose_base_edge_map = BLI_BITMAP_NEW(base_mesh->totedge,
|
||||
"non_loose_base_edge_map");
|
||||
const blender::bke::LooseEdgeCache &loose_edges = base_mesh->loose_edges();
|
||||
reshape_smooth_context->loose_base_edges = loose_edges.is_loose_bits;
|
||||
|
||||
int num_used_edges = 0;
|
||||
for (const int poly_index : base_polys.index_range()) {
|
||||
for (const int edge : base_corner_edges.slice(base_polys[poly_index])) {
|
||||
if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, edge)) {
|
||||
BLI_BITMAP_ENABLE(reshape_smooth_context->non_loose_base_edge_map, edge);
|
||||
|
||||
const float crease = get_effective_crease(reshape_smooth_context, edge);
|
||||
if (crease > 0.0f) {
|
||||
++num_used_edges;
|
||||
}
|
||||
}
|
||||
for (const int edge : blender::IndexRange(base_mesh->totedge)) {
|
||||
if (loose_edges.count > 0 && loose_edges.is_loose_bits[edge]) {
|
||||
continue;
|
||||
}
|
||||
const float crease = get_effective_crease(reshape_smooth_context, edge);
|
||||
if (crease == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
num_used_edges++;
|
||||
}
|
||||
|
||||
const int resolution = get_reshape_level_resolution(reshape_context);
|
||||
|
|
|
@ -2718,7 +2718,10 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
|
|||
copy_v3_v3(ob_tar->scale, ob_src->scale);
|
||||
}
|
||||
|
||||
Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplicate_options)
|
||||
Object *BKE_object_duplicate(Main *bmain,
|
||||
Object *ob,
|
||||
eDupli_ID_Flags dupflag,
|
||||
uint duplicate_options)
|
||||
{
|
||||
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
|
||||
const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0;
|
||||
|
|
|
@ -758,10 +758,10 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
|
|||
|
||||
static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb)
|
||||
{
|
||||
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
|
||||
LISTBASE_FOREACH (LayerCollection *, lc, lb) {
|
||||
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad
|
||||
* anyway... */
|
||||
const int cb_flag = (lc->collection != nullptr &&
|
||||
(data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0 &&
|
||||
(lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
|
||||
IDWALK_CB_EMBEDDED_NOT_OWNING :
|
||||
IDWALK_CB_NOP;
|
||||
|
|
|
@ -85,8 +85,9 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet *
|
|||
|
||||
void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area)
|
||||
{
|
||||
const bool is_readonly = (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_READONLY) !=
|
||||
0;
|
||||
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
|
||||
const bool is_readonly = (data_flags & IDWALK_READONLY) != 0;
|
||||
const bool allow_pointer_access = (data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0;
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP);
|
||||
|
||||
|
@ -190,7 +191,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
while ((tselem = BLI_mempool_iterstep(&iter))) {
|
||||
/* Do not try to restore non-ID pointers (drivers/sequence/etc.). */
|
||||
if (TSE_IS_REAL_ID(tselem)) {
|
||||
const int cb_flag = (tselem->id != NULL &&
|
||||
const int cb_flag = (tselem->id != NULL && allow_pointer_access &&
|
||||
(tselem->id->flag & LIB_EMBEDDED_DATA) != 0) ?
|
||||
IDWALK_CB_EMBEDDED_NOT_OWNING :
|
||||
IDWALK_CB_NOP;
|
||||
|
@ -209,7 +210,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
}
|
||||
case SPACE_NODE: {
|
||||
SpaceNode *snode = (SpaceNode *)sl;
|
||||
const bool is_embedded_nodetree = snode->id != NULL &&
|
||||
const bool is_embedded_nodetree = snode->id != NULL && allow_pointer_access &&
|
||||
ntreeFromID(snode->id) == snode->nodetree;
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP);
|
||||
|
@ -227,8 +228,11 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
/* Embedded ID pointers are not remapped (besides exceptions), ensure it still matches
|
||||
* actual data. Note that `snode->id` was already processed (and therefore potentially
|
||||
* remapped) above.*/
|
||||
if (!is_readonly && path != NULL) {
|
||||
path->nodetree = snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
|
||||
if (!is_readonly) {
|
||||
snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
|
||||
if (path != NULL) {
|
||||
path->nodetree = snode->nodetree;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -238,10 +242,18 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
}
|
||||
}
|
||||
|
||||
/* Both `snode->id` and `snode->nodetree` have been remapped now, sotheir data can be
|
||||
* accessed. */
|
||||
BLI_assert(snode->id == NULL || snode->nodetree == NULL ||
|
||||
(snode->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0 ||
|
||||
snode->nodetree == ntreeFromID(snode->id));
|
||||
|
||||
if (path != NULL) {
|
||||
for (path = path->next; path != NULL; path = path->next) {
|
||||
BLI_assert(path->nodetree != NULL);
|
||||
BLI_assert((path->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0);
|
||||
if ((data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0) {
|
||||
BLI_assert((path->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0);
|
||||
}
|
||||
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE);
|
||||
|
||||
|
@ -270,13 +282,16 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
|
|||
snode->edittree = NULL;
|
||||
}
|
||||
}
|
||||
/* NOTE: It is disputable whether this should be called here, especially when editing it is
|
||||
* allowed? But for now, for sake of consistency, do it in any case. */
|
||||
if (is_embedded_nodetree && snode->edittree == snode->nodetree) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_EMBEDDED_NOT_OWNING);
|
||||
}
|
||||
else {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
|
||||
/* Only process this pointer in readonly case, otherwise could lead to a bad
|
||||
* double-remapping e.g. */
|
||||
if (is_embedded_nodetree && snode->edittree == snode->nodetree) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
|
||||
data, snode->edittree, IDWALK_CB_EMBEDDED_NOT_OWNING);
|
||||
}
|
||||
else {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_timeit.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
|
@ -78,46 +78,30 @@ bool BKE_subdiv_eval_begin(Subdiv *subdiv,
|
|||
}
|
||||
|
||||
static void set_coarse_positions(Subdiv *subdiv,
|
||||
const Mesh *mesh,
|
||||
const float (*coarse_vertex_cos)[3])
|
||||
const blender::Span<blender::float3> positions,
|
||||
const blender::bke::LooseVertCache &verts_no_face)
|
||||
{
|
||||
const float(*positions)[3] = BKE_mesh_vert_positions(mesh);
|
||||
const blender::OffsetIndices polys = mesh->polys();
|
||||
const blender::Span<int> corner_verts = mesh->corner_verts();
|
||||
/* Mark vertices which needs new coordinates. */
|
||||
/* TODO(sergey): This is annoying to calculate this on every update,
|
||||
* maybe it's better to cache this mapping. Or make it possible to have
|
||||
* OpenSubdiv's vertices match mesh ones? */
|
||||
BLI_bitmap *vertex_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
|
||||
for (const int i : polys.index_range()) {
|
||||
for (const int vert : corner_verts.slice(polys[i])) {
|
||||
BLI_BITMAP_ENABLE(vertex_used_map, vert);
|
||||
}
|
||||
using namespace blender;
|
||||
OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
|
||||
if (verts_no_face.count == 0) {
|
||||
evaluator->setCoarsePositions(
|
||||
evaluator, reinterpret_cast<const float *>(positions.data()), 0, positions.size());
|
||||
return;
|
||||
}
|
||||
/* Use a temporary buffer so we do not upload vertices one at a time to the GPU. */
|
||||
float(*buffer)[3] = static_cast<float(*)[3]>(
|
||||
MEM_mallocN(sizeof(float[3]) * mesh->totvert, __func__));
|
||||
int manifold_vertex_count = 0;
|
||||
for (int vertex_index = 0, manifold_vertex_index = 0; vertex_index < mesh->totvert;
|
||||
vertex_index++) {
|
||||
if (!BLI_BITMAP_TEST_BOOL(vertex_used_map, vertex_index)) {
|
||||
Array<float3> used_vert_positions(positions.size() - verts_no_face.count);
|
||||
const BitSpan bits = verts_no_face.is_loose_bits;
|
||||
int used_vert_count = 0;
|
||||
for (const int vert : positions.index_range()) {
|
||||
if (bits[vert]) {
|
||||
continue;
|
||||
}
|
||||
const float *vertex_co;
|
||||
if (coarse_vertex_cos != nullptr) {
|
||||
vertex_co = coarse_vertex_cos[vertex_index];
|
||||
}
|
||||
else {
|
||||
vertex_co = positions[vertex_index];
|
||||
}
|
||||
copy_v3_v3(&buffer[manifold_vertex_index][0], vertex_co);
|
||||
manifold_vertex_index++;
|
||||
manifold_vertex_count++;
|
||||
used_vert_positions[used_vert_count] = positions[vert];
|
||||
used_vert_count++;
|
||||
}
|
||||
subdiv->evaluator->setCoarsePositions(
|
||||
subdiv->evaluator, &buffer[0][0], 0, manifold_vertex_count);
|
||||
MEM_freeN(vertex_used_map);
|
||||
MEM_freeN(buffer);
|
||||
evaluator->setCoarsePositions(evaluator,
|
||||
reinterpret_cast<const float *>(used_vert_positions.data()),
|
||||
0,
|
||||
used_vert_positions.size());
|
||||
}
|
||||
|
||||
/* Context which is used to fill face varying data in parallel. */
|
||||
|
@ -242,13 +226,20 @@ bool BKE_subdiv_eval_refine_from_mesh(Subdiv *subdiv,
|
|||
const Mesh *mesh,
|
||||
const float (*coarse_vertex_cos)[3])
|
||||
{
|
||||
using namespace blender;
|
||||
if (subdiv->evaluator == nullptr) {
|
||||
/* NOTE: This situation is supposed to be handled by begin(). */
|
||||
BLI_assert_msg(0, "Is not supposed to happen");
|
||||
return false;
|
||||
}
|
||||
/* Set coordinates of base mesh vertices. */
|
||||
set_coarse_positions(subdiv, mesh, coarse_vertex_cos);
|
||||
set_coarse_positions(
|
||||
subdiv,
|
||||
coarse_vertex_cos ?
|
||||
Span(reinterpret_cast<const float3 *>(coarse_vertex_cos), mesh->totvert) :
|
||||
mesh->vert_positions(),
|
||||
mesh->verts_no_face());
|
||||
|
||||
/* Set face-varying data to UV maps. */
|
||||
const int num_uv_layers = CustomData_number_of_layers(&mesh->ldata, CD_PROP_FLOAT2);
|
||||
for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) {
|
||||
|
|
|
@ -56,6 +56,8 @@
|
|||
|
||||
#include "RE_texture.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
static void texture_init_data(ID *id)
|
||||
|
@ -100,6 +102,8 @@ static void texture_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
|
|||
texture_dst->nodetree->owner_id = &texture_dst->id;
|
||||
}
|
||||
|
||||
BLI_listbase_clear((ListBase *)&texture_dst->drawdata);
|
||||
|
||||
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
|
||||
BKE_previewimg_id_copy(&texture_dst->id, &texture_src->id);
|
||||
}
|
||||
|
@ -112,6 +116,8 @@ static void texture_free_data(ID *id)
|
|||
{
|
||||
Tex *texture = (Tex *)id;
|
||||
|
||||
DRW_drawdata_free(id);
|
||||
|
||||
/* is no lib link block, but texture extension */
|
||||
if (texture->nodetree) {
|
||||
ntreeFreeEmbeddedTree(texture->nodetree);
|
||||
|
|
|
@ -3117,8 +3117,8 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
|
|||
id,
|
||||
id_old,
|
||||
true,
|
||||
ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_UPDATE_TAGGING |
|
||||
ID_REMAP_SKIP_USER_REFCOUNT);
|
||||
(ID_REMAP_NO_ORIG_POINTERS_ACCESS | ID_REMAP_SKIP_NEVER_NULL_USAGE |
|
||||
ID_REMAP_SKIP_UPDATE_TAGGING | ID_REMAP_SKIP_USER_REFCOUNT));
|
||||
|
||||
/* Special temporary usage of this pointer, necessary for the `undo_preserve` call after
|
||||
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of
|
||||
|
|
|
@ -70,12 +70,14 @@ set(SRC
|
|||
algorithms/COM_algorithm_smaa.hh
|
||||
algorithms/COM_algorithm_symmetric_separable_blur.hh
|
||||
|
||||
cached_resources/intern/cached_texture.cc
|
||||
cached_resources/intern/morphological_distance_feather_weights.cc
|
||||
cached_resources/intern/smaa_precomputed_textures.cc
|
||||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
|
||||
cached_resources/COM_cached_resource.hh
|
||||
cached_resources/COM_cached_texture.hh
|
||||
cached_resources/COM_morphological_distance_feather_weights.hh
|
||||
cached_resources/COM_smaa_precomputed_textures.hh
|
||||
cached_resources/COM_symmetric_blur_weights.hh
|
||||
|
@ -248,4 +250,16 @@ endforeach()
|
|||
set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/compositor_shader_create_info_list.hh")
|
||||
file(GENERATE OUTPUT ${shader_create_info_list_file} CONTENT "${SHADER_CREATE_INFOS_CONTENT}")
|
||||
|
||||
if(WITH_TBB)
|
||||
list(APPEND INC_SYS
|
||||
${TBB_INCLUDE_DIRS}
|
||||
)
|
||||
add_definitions(-DWITH_TBB)
|
||||
if(WIN32)
|
||||
# TBB includes Windows.h which will define min/max macros
|
||||
# that will collide with the stl versions.
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
|
@ -70,6 +71,14 @@ class Context {
|
|||
* appropriate place, which can be directly in the UI or just logged to the output stream. */
|
||||
virtual void set_info_message(StringRef message) const = 0;
|
||||
|
||||
/* Returns the ID recalculate flag of the given ID and reset it to zero. The given ID is assumed
|
||||
* to be one that has a DrawDataList and conforms to the IdDdtTemplate.
|
||||
*
|
||||
* The ID recalculate flag is a mechanism through which one can identify if an ID has changed
|
||||
* since the last time the flag was reset, hence why the method reset the flag after querying it,
|
||||
* that is, to ready it to track the next change. */
|
||||
virtual IDRecalcFlag query_id_recalc_flag(ID *id) const = 0;
|
||||
|
||||
/* Get the size of the compositing region. See get_compositing_region(). */
|
||||
int2 get_compositing_region_size() const;
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
|
@ -14,6 +19,8 @@
|
|||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Static Cache Manager
|
||||
*
|
||||
|
@ -48,6 +55,11 @@ class StaticCacheManager {
|
|||
Map<MorphologicalDistanceFeatherWeightsKey, std::unique_ptr<MorphologicalDistanceFeatherWeights>>
|
||||
morphological_distance_feather_weights_;
|
||||
|
||||
/* A nested map that stores all CachedTexture cached resources. The outer map identifies the
|
||||
* textures using their ID name, while the inner map identifies the textures using their
|
||||
* parameters. */
|
||||
Map<std::string, Map<CachedTextureKey, std::unique_ptr<CachedTexture>>> cached_textures_;
|
||||
|
||||
/* A unique pointers that stores the cached SMAAPrecomputedTextures, if one is cached. */
|
||||
std::unique_ptr<SMAAPrecomputedTextures> smaa_precomputed_textures_;
|
||||
|
||||
|
@ -77,6 +89,15 @@ class StaticCacheManager {
|
|||
MorphologicalDistanceFeatherWeights &get_morphological_distance_feather_weights(int type,
|
||||
int radius);
|
||||
|
||||
/* Check if the given texture ID has changed since the last time it was retrieved through its
|
||||
* recalculate flag, and if so, invalidate its corresponding cached textures and reset the
|
||||
* recalculate flag to ready it to track the next change. Then, check if there is an available
|
||||
* CachedTexture cached resource with the given parameters in the manager, if one exists, return
|
||||
* it, otherwise, return a newly created one and add it to the manager. In both cases, tag the
|
||||
* cached resource as needed to keep it cached for the next evaluation. */
|
||||
CachedTexture &get_cached_texture(
|
||||
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
|
||||
|
||||
/* Check if a cached SMAA precomputed texture exists, if it does, return it, otherwise, return
|
||||
* a newly created one and store it in the manager. In both cases, tag the cached resource as
|
||||
* needed to keep it cached for the next evaluation. */
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Cached Texture Key.
|
||||
*/
|
||||
class CachedTextureKey {
|
||||
public:
|
||||
int2 size;
|
||||
float2 offset;
|
||||
float2 scale;
|
||||
|
||||
CachedTextureKey(int2 size, float2 offset, float2 scale);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b);
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Cached Texture.
|
||||
*
|
||||
* A cached resource that computes and caches a GPU texture containing the the result of evaluating
|
||||
* the given texture ID on a space that spans the given size, modified by the given offset and
|
||||
* scale. */
|
||||
class CachedTexture : public CachedResource {
|
||||
private:
|
||||
GPUTexture *color_texture_ = nullptr;
|
||||
GPUTexture *value_texture_ = nullptr;
|
||||
|
||||
public:
|
||||
CachedTexture(Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
|
||||
|
||||
~CachedTexture();
|
||||
|
||||
GPUTexture *color_texture();
|
||||
|
||||
GPUTexture *value_texture();
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,102 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "RE_texture.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Cached Texture Key.
|
||||
*/
|
||||
|
||||
CachedTextureKey::CachedTextureKey(int2 size, float2 offset, float2 scale)
|
||||
: size(size), offset(offset), scale(scale)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t CachedTextureKey::hash() const
|
||||
{
|
||||
return get_default_hash_3(size, offset, scale);
|
||||
}
|
||||
|
||||
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b)
|
||||
{
|
||||
return a.size == b.size && a.offset == b.offset && a.scale == b.scale;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Cached Texture.
|
||||
*/
|
||||
|
||||
CachedTexture::CachedTexture(
|
||||
Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
|
||||
{
|
||||
Array<float4> color_pixels(size.x * size.y);
|
||||
Array<float> value_pixels(size.x * size.y);
|
||||
threading::parallel_for(IndexRange(size.y), 1, [&](const IndexRange sub_y_range) {
|
||||
for (const int64_t y : sub_y_range) {
|
||||
for (const int64_t x : IndexRange(size.x)) {
|
||||
/* Compute the coordinates in the [0, 1] range and add 0.5 to evaluate the texture at the
|
||||
* center of pixels in case it was interpolated. */
|
||||
float2 coordinates = ((float2(x, y) + 0.5f) / float2(size)) * 2.0f - 1.0f;
|
||||
/* Note that it is expected that the offset is scaled by the scale. */
|
||||
coordinates = (coordinates + offset) * scale;
|
||||
TexResult texture_result;
|
||||
BKE_texture_get_value(scene, texture, coordinates, &texture_result, true);
|
||||
color_pixels[y * size.x + x] = float4(texture_result.trgba);
|
||||
value_pixels[y * size.x + x] = texture_result.talpha ? texture_result.trgba[3] :
|
||||
texture_result.tin;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
color_texture_ = GPU_texture_create_2d("Cached Color Texture",
|
||||
size.x,
|
||||
size.y,
|
||||
1,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
*color_pixels.data());
|
||||
|
||||
value_texture_ = GPU_texture_create_2d("Cached Value Texture",
|
||||
size.x,
|
||||
size.y,
|
||||
1,
|
||||
GPU_R16F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
value_pixels.data());
|
||||
}
|
||||
|
||||
CachedTexture::~CachedTexture()
|
||||
{
|
||||
GPU_texture_free(color_texture_);
|
||||
GPU_texture_free(value_texture_);
|
||||
}
|
||||
|
||||
GPUTexture *CachedTexture::color_texture()
|
||||
{
|
||||
return color_texture_;
|
||||
}
|
||||
|
||||
GPUTexture *CachedTexture::value_texture()
|
||||
{
|
||||
return value_texture_;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
|
@ -23,6 +28,12 @@ void StaticCacheManager::reset()
|
|||
symmetric_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
symmetric_separable_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
morphological_distance_feather_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
for (auto &cached_textures_for_id : cached_textures_.values()) {
|
||||
cached_textures_for_id.remove_if([](auto item) { return !item.value->needed; });
|
||||
}
|
||||
cached_textures_.remove_if([](auto item) { return item.value.is_empty(); });
|
||||
|
||||
if (smaa_precomputed_textures_ && !smaa_precomputed_textures_->needed) {
|
||||
smaa_precomputed_textures_.reset();
|
||||
}
|
||||
|
@ -38,6 +49,11 @@ void StaticCacheManager::reset()
|
|||
for (auto &value : morphological_distance_feather_weights_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
for (auto &cached_textures_for_id : cached_textures_.values()) {
|
||||
for (auto &value : cached_textures_for_id.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
if (smaa_precomputed_textures_) {
|
||||
smaa_precomputed_textures_->needed = false;
|
||||
}
|
||||
|
@ -78,6 +94,24 @@ MorphologicalDistanceFeatherWeights &StaticCacheManager::
|
|||
return weights;
|
||||
}
|
||||
|
||||
CachedTexture &StaticCacheManager::get_cached_texture(
|
||||
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
|
||||
{
|
||||
const CachedTextureKey key(size, offset, scale);
|
||||
|
||||
auto &cached_textures_for_id = cached_textures_.lookup_or_add_default(texture->id.name);
|
||||
|
||||
if (context.query_id_recalc_flag(reinterpret_cast<ID *>(texture)) & ID_RECALC_ALL) {
|
||||
cached_textures_for_id.clear();
|
||||
}
|
||||
|
||||
auto &cached_texture = *cached_textures_for_id.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<CachedTexture>(texture, scene, size, offset, scale); });
|
||||
|
||||
cached_texture.needed = true;
|
||||
return cached_texture;
|
||||
}
|
||||
|
||||
SMAAPrecomputedTextures &StaticCacheManager::get_smaa_precomputed_textures()
|
||||
{
|
||||
if (!smaa_precomputed_textures_) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
@ -143,6 +144,15 @@ class Context : public realtime_compositor::Context {
|
|||
{
|
||||
message.copy(info_message_, GPU_INFO_SIZE);
|
||||
}
|
||||
|
||||
IDRecalcFlag query_id_recalc_flag(ID *id) const override
|
||||
{
|
||||
DrawEngineType *owner = &draw_engine_compositor_type;
|
||||
DrawData *draw_data = DRW_drawdata_ensure(id, owner, sizeof(DrawData), nullptr, nullptr);
|
||||
IDRecalcFlag recalc_flag = IDRecalcFlag(draw_data->recalc);
|
||||
draw_data->recalc = IDRecalcFlag(0);
|
||||
return recalc_flag;
|
||||
}
|
||||
};
|
||||
|
||||
class Engine {
|
||||
|
|
|
@ -832,6 +832,7 @@ static bool id_type_can_have_drawdata(const short id_type)
|
|||
case ID_OB:
|
||||
case ID_WO:
|
||||
case ID_SCE:
|
||||
case ID_TE:
|
||||
return true;
|
||||
|
||||
/* no DrawData */
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -3158,12 +3158,14 @@ static bool ui_textedit_insert_buf(uiBut *but,
|
|||
return changed;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
|
||||
{
|
||||
BLI_assert(isascii(ascii));
|
||||
const char buf[2] = {ascii, '\0'};
|
||||
return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ui_textedit_move(uiBut *but,
|
||||
uiHandleButtonData *data,
|
||||
|
@ -3940,9 +3942,6 @@ static void ui_do_but_textedit(
|
|||
else if (event->type == WM_IME_COMPOSITE_END) {
|
||||
changed = true;
|
||||
}
|
||||
#else
|
||||
/* Prevent the function from being unused. */
|
||||
(void)ui_textedit_insert_ascii;
|
||||
#endif
|
||||
|
||||
if (changed) {
|
||||
|
|
|
@ -260,6 +260,7 @@ void SCULPT_filter_cache_free(SculptSession *ss)
|
|||
MEM_SAFE_FREE(ss->filter_cache->limit_surface_co);
|
||||
MEM_SAFE_FREE(ss->filter_cache->pre_smoothed_color);
|
||||
MEM_delete<FilterCache>(ss->filter_cache);
|
||||
ss->filter_cache = nullptr;
|
||||
}
|
||||
|
||||
typedef enum eSculptMeshFilterType {
|
||||
|
|
|
@ -1246,13 +1246,7 @@ static void snap_source_active_fn(TransInfo *t)
|
|||
{
|
||||
/* Only need to calculate once */
|
||||
if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
|
||||
if (t->around == V3D_AROUND_ACTIVE) {
|
||||
/* Just copy the already calculated active center. */
|
||||
copy_v3_v3(t->tsnap.snap_source, t->center_global);
|
||||
TargetSnapOffset(t, nullptr);
|
||||
t->tsnap.status |= SNAP_SOURCE_FOUND;
|
||||
}
|
||||
else if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
|
||||
if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
|
||||
TargetSnapOffset(t, nullptr);
|
||||
t->tsnap.status |= SNAP_SOURCE_FOUND;
|
||||
}
|
||||
|
|
|
@ -151,13 +151,36 @@ shaderc::Compiler &VKBackend::get_shaderc_compiler()
|
|||
return shaderc_compiler_;
|
||||
}
|
||||
|
||||
void VKBackend::capabilities_init(VKContext & /*context*/)
|
||||
void VKBackend::capabilities_init(VKContext &context)
|
||||
{
|
||||
const VkPhysicalDeviceLimits limits = context.physical_device_limits_get();
|
||||
|
||||
/* Reset all capabilities from previous context. */
|
||||
GCaps = {};
|
||||
GCaps.compute_shader_support = true;
|
||||
GCaps.shader_storage_buffer_objects_support = true;
|
||||
GCaps.shader_image_load_store_support = true;
|
||||
|
||||
GCaps.max_texture_size = max_ii(limits.maxImageDimension1D, limits.maxImageDimension2D);
|
||||
GCaps.max_texture_3d_size = limits.maxImageDimension3D;
|
||||
GCaps.max_texture_layers = limits.maxImageArrayLayers;
|
||||
GCaps.max_textures = limits.maxDescriptorSetSampledImages;
|
||||
GCaps.max_textures_vert = limits.maxPerStageDescriptorSampledImages;
|
||||
GCaps.max_textures_geom = limits.maxPerStageDescriptorSampledImages;
|
||||
GCaps.max_textures_frag = limits.maxPerStageDescriptorSampledImages;
|
||||
GCaps.max_samplers = limits.maxSamplerAllocationCount;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
GCaps.max_work_group_count[i] = limits.maxComputeWorkGroupCount[i];
|
||||
GCaps.max_work_group_size[i] = limits.maxComputeWorkGroupSize[i];
|
||||
}
|
||||
GCaps.max_uniforms_vert = limits.maxPerStageDescriptorUniformBuffers;
|
||||
GCaps.max_uniforms_frag = limits.maxPerStageDescriptorUniformBuffers;
|
||||
GCaps.max_batch_indices = limits.maxDrawIndirectCount;
|
||||
GCaps.max_batch_vertices = limits.maxDrawIndexedIndexValue;
|
||||
GCaps.max_vertex_attribs = limits.maxVertexInputAttributes;
|
||||
GCaps.max_varying_floats = limits.maxVertexOutputComponents;
|
||||
GCaps.max_shader_storage_buffer_bindings = limits.maxPerStageDescriptorStorageBuffers;
|
||||
GCaps.max_compute_shader_storage_blocks = limits.maxPerStageDescriptorStorageBuffers;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -39,10 +39,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
|
|||
|
||||
/* Initialize the memory allocator. */
|
||||
VmaAllocatorCreateInfo info = {};
|
||||
/* Should use same vulkan version as GHOST (1.2), but set to 1.0 as 1.2 requires
|
||||
* correct extensions and functions to be found by VMA, which isn't working as expected and
|
||||
* requires more research. To continue development we lower the API to version 1.0. */
|
||||
info.vulkanApiVersion = VK_API_VERSION_1_0;
|
||||
info.vulkanApiVersion = VK_API_VERSION_1_2;
|
||||
info.physicalDevice = vk_physical_device_;
|
||||
info.device = vk_device_;
|
||||
info.instance = vk_instance_;
|
||||
|
|
|
@ -153,6 +153,8 @@ typedef struct Tex {
|
|||
ID id;
|
||||
/** Animation data (must be immediately after id for utilities to use it). */
|
||||
struct AnimData *adt;
|
||||
/* runtime (must be immediately after id for utilities to use it). */
|
||||
DrawDataList drawdata;
|
||||
|
||||
float noisesize, turbul;
|
||||
float bright, contrast, saturation, rfac, gfac, bfac;
|
||||
|
@ -264,14 +266,14 @@ typedef struct ColorMapping {
|
|||
#define TEX_STUCCI 6
|
||||
#define TEX_NOISE 7
|
||||
#define TEX_IMAGE 8
|
||||
//#define TEX_PLUGIN 9 /* Deprecated */
|
||||
//#define TEX_ENVMAP 10 /* Deprecated */
|
||||
// #define TEX_PLUGIN 9 /* Deprecated */
|
||||
// #define TEX_ENVMAP 10 /* Deprecated */
|
||||
#define TEX_MUSGRAVE 11
|
||||
#define TEX_VORONOI 12
|
||||
#define TEX_DISTNOISE 13
|
||||
//#define TEX_POINTDENSITY 14 /* Deprecated */
|
||||
//#define TEX_VOXELDATA 15 /* Deprecated */
|
||||
//#define TEX_OCEAN 16 /* Deprecated */
|
||||
// #define TEX_POINTDENSITY 14 /* Deprecated */
|
||||
// #define TEX_VOXELDATA 15 /* Deprecated */
|
||||
// #define TEX_OCEAN 16 /* Deprecated */
|
||||
|
||||
/* musgrave stype */
|
||||
#define TEX_MFRACTAL 0
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -50,6 +52,7 @@ typedef enum eDupli_ID_Flags {
|
|||
/* Duplicate (and hence make local) linked data. */
|
||||
USER_DUP_LINKED_ID = (1 << 30),
|
||||
} eDupli_ID_Flags;
|
||||
ENUM_OPERATORS(eDupli_ID_Flags, USER_DUP_LINKED_ID)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -27,8 +27,16 @@ NODE_STORAGE_FUNCS(NodeBoxMask)
|
|||
|
||||
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Float>(N_("Mask"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Float>(N_("Value"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.compositor_domain_priority(1);
|
||||
b.add_output<decl::Float>(N_("Mask"));
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,16 @@ NODE_STORAGE_FUNCS(NodeEllipseMask)
|
|||
|
||||
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
|
||||
b.add_input<decl::Float>(N_("Mask"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.compositor_domain_priority(0);
|
||||
b.add_input<decl::Float>(N_("Value"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.compositor_domain_priority(1);
|
||||
b.add_output<decl::Float>(N_("Mask"));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
|
@ -17,12 +19,17 @@ namespace blender::nodes::node_composite_texture_cc {
|
|||
|
||||
static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
|
||||
b.add_input<decl::Vector>(N_("Offset"))
|
||||
.min(-2.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_TRANSLATION)
|
||||
.compositor_expects_single_value();
|
||||
b.add_input<decl::Vector>(N_("Scale"))
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.min(-10.0f)
|
||||
.max(10.0f)
|
||||
.subtype(PROP_XYZ);
|
||||
.subtype(PROP_XYZ)
|
||||
.compositor_expects_single_value();
|
||||
b.add_output<decl::Float>(N_("Value"));
|
||||
b.add_output<decl::Color>(N_("Color"));
|
||||
}
|
||||
|
@ -35,9 +42,46 @@ class TextureOperation : public NodeOperation {
|
|||
|
||||
void execute() override
|
||||
{
|
||||
get_result("Value").allocate_invalid();
|
||||
get_result("Color").allocate_invalid();
|
||||
context().set_info_message("Viewport compositor setup not fully supported");
|
||||
Result &color_result = get_result("Color");
|
||||
Result &value_result = get_result("Value");
|
||||
if (!get_texture()) {
|
||||
if (color_result.should_compute()) {
|
||||
color_result.allocate_invalid();
|
||||
}
|
||||
if (value_result.should_compute()) {
|
||||
value_result.allocate_invalid();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
CachedTexture &cached_texture = context().cache_manager().get_cached_texture(
|
||||
context(),
|
||||
get_texture(),
|
||||
context().get_scene(),
|
||||
domain.size,
|
||||
get_input("Offset").get_vector_value_default(float4(0.0f)).xy(),
|
||||
get_input("Scale").get_vector_value_default(float4(0.0f)).xy());
|
||||
|
||||
if (color_result.should_compute()) {
|
||||
color_result.allocate_texture(domain);
|
||||
GPU_texture_copy(color_result.texture(), cached_texture.color_texture());
|
||||
}
|
||||
|
||||
if (value_result.should_compute()) {
|
||||
value_result.allocate_texture(domain);
|
||||
GPU_texture_copy(value_result.texture(), cached_texture.value_texture());
|
||||
}
|
||||
}
|
||||
|
||||
Domain compute_domain() override
|
||||
{
|
||||
return Domain(context().get_compositing_region_size());
|
||||
}
|
||||
|
||||
Tex *get_texture()
|
||||
{
|
||||
return (Tex *)bnode().id;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -58,8 +102,6 @@ void register_node_type_cmp_texture()
|
|||
ntype.declare = file_ns::cmp_node_texture_declare;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.realtime_compositor_unsupported_message = N_(
|
||||
"Node not supported in the Viewport compositor");
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -28,18 +28,9 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||
{
|
||||
b.add_input<decl::String>(N_("String"));
|
||||
b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
|
||||
b.add_input<decl::Float>(N_("Character Spacing"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.subtype(PROP_DISTANCE);
|
||||
b.add_input<decl::Float>(N_("Word Spacing"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.subtype(PROP_DISTANCE);
|
||||
b.add_input<decl::Float>(N_("Line Spacing"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.subtype(PROP_DISTANCE);
|
||||
b.add_input<decl::Float>(N_("Character Spacing")).default_value(1.0f).min(0.0f);
|
||||
b.add_input<decl::Float>(N_("Word Spacing")).default_value(1.0f).min(0.0f);
|
||||
b.add_input<decl::Float>(N_("Line Spacing")).default_value(1.0f).min(0.0f);
|
||||
b.add_input<decl::Float>(N_("Text Box Width"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -2619,6 +2619,8 @@ void *WM_opengl_context_create(void)
|
|||
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
|
||||
|
||||
GHOST_GLSettings glSettings = {0};
|
||||
const eGPUBackendType gpu_backend = GPU_backend_type_selection_get();
|
||||
glSettings.context_type = wm_ghost_drawing_context_type(gpu_backend);
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
glSettings.flags |= GHOST_glDebugContext;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue