Refactoring: Corrections and unifications in mathematics vfont gizmos #107193
|
@ -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,
|
||||
|
|
|
@ -550,7 +550,7 @@ static void log_kernel_features(const uint features)
|
|||
<< "\n";
|
||||
VLOG_INFO << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE)
|
||||
<< "\n";
|
||||
VLOG_INFO << "Use MNEE" << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
|
||||
VLOG_INFO << "Use MNEE " << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
|
||||
VLOG_INFO << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT)
|
||||
<< "\n";
|
||||
VLOG_INFO << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n";
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -23,7 +23,7 @@ std::string normalize_directory_path(StringRef directory)
|
|||
directory.data(),
|
||||
/* + 1 for null terminator. */
|
||||
std::min(directory.size() + 1, int64_t(sizeof(dir_normalized))));
|
||||
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized));
|
||||
BLI_path_normalize_dir(dir_normalized, sizeof(dir_normalized));
|
||||
return std::string(dir_normalized);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ std::string normalize_path(StringRefNull path, int64_t max_len)
|
|||
|
||||
char *buf = BLI_strdupn(path.c_str(), len);
|
||||
BLI_path_slash_native(buf);
|
||||
BLI_path_normalize(nullptr, buf);
|
||||
BLI_path_normalize(buf);
|
||||
|
||||
std::string normalized_path = buf;
|
||||
MEM_freeN(buf);
|
||||
|
|
|
@ -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 -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,8 +11,6 @@
|
|||
|
||||
# include <mutex>
|
||||
|
||||
# include "MEM_guardedalloc.h"
|
||||
|
||||
# include "BLI_array.hh"
|
||||
# include "BLI_bit_vector.hh"
|
||||
# include "BLI_bounds_types.hh"
|
||||
|
@ -22,7 +20,6 @@
|
|||
# include "BLI_span.hh"
|
||||
# include "BLI_vector.hh"
|
||||
|
||||
# include "DNA_customdata_types.h"
|
||||
# include "DNA_meshdata_types.h"
|
||||
|
||||
struct BVHCache;
|
||||
|
@ -175,7 +172,7 @@ struct MeshRuntime {
|
|||
SharedCache<LooseEdgeCache> loose_edges_cache;
|
||||
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */
|
||||
SharedCache<LooseVertCache> loose_verts_cache;
|
||||
/** Cache of data about vertices not used by faces. See #Mesh::loose_verts(). */
|
||||
/** Cache of data about vertices not used by faces. See #Mesh::verts_no_face(). */
|
||||
SharedCache<LooseVertCache> verts_no_face_cache;
|
||||
|
||||
/**
|
||||
|
|
|
@ -386,7 +386,7 @@ static bool get_path_local_ex(char *targetpath,
|
|||
char osx_resourses[FILE_MAX + 4 + 9];
|
||||
BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources");
|
||||
/* Remove the '/../' added above. */
|
||||
BLI_path_normalize(NULL, osx_resourses);
|
||||
BLI_path_normalize(osx_resourses);
|
||||
path_base = osx_resourses;
|
||||
#endif
|
||||
return test_path(targetpath,
|
||||
|
@ -871,7 +871,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
|
|||
BLI_path_program_search(fullname, maxlen, name);
|
||||
}
|
||||
/* Remove "/./" and "/../" so string comparisons can be used on the path. */
|
||||
BLI_path_normalize(NULL, fullname);
|
||||
BLI_path_normalize(fullname);
|
||||
|
||||
# if defined(DEBUG)
|
||||
if (!STREQ(name, fullname)) {
|
||||
|
@ -890,7 +890,7 @@ void BKE_appdir_program_path_init(const char *argv0)
|
|||
* which must point to the Python module for data-files to be detected. */
|
||||
STRNCPY(g_app.program_filepath, argv0);
|
||||
BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
|
||||
BLI_path_normalize(NULL, g_app.program_filepath);
|
||||
BLI_path_normalize(g_app.program_filepath);
|
||||
|
||||
if (g_app.program_dirname[0] == '\0') {
|
||||
/* First time initializing, the file binary path isn't valid from a Python module.
|
||||
|
|
|
@ -441,7 +441,7 @@ static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|||
return false;
|
||||
}
|
||||
|
||||
BLI_path_normalize(NULL, filepath);
|
||||
BLI_path_normalize(filepath);
|
||||
|
||||
/* This may fail, if so it's fine to leave absolute since the path is still valid. */
|
||||
BLI_path_rel(filepath, data->basedir_dst);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -137,7 +137,7 @@ static bool lib_id_library_local_paths_callback(BPathForeachPathData *bpath_data
|
|||
/* Path was relative and is now absolute. Remap.
|
||||
* Important BLI_path_normalize runs before the path is made relative
|
||||
* because it won't work for paths that start with "//../" */
|
||||
BLI_path_normalize(base_new, filepath);
|
||||
BLI_path_normalize(filepath);
|
||||
BLI_path_rel(filepath, base_new);
|
||||
BLI_strncpy(r_path_dst, filepath, FILE_MAX);
|
||||
return true;
|
||||
|
@ -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;
|
||||
|
|
|
@ -115,14 +115,14 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
/* This is a direct copy of a main mesh, so for now it has the same topology. */
|
||||
mesh_dst->runtime->deformed_only = true;
|
||||
}
|
||||
/* This option is set for run-time meshes that have been copied from the current objects mode.
|
||||
/* This option is set for run-time meshes that have been copied from the current object's mode.
|
||||
* Currently this is used for edit-mesh although it could be used for sculpt or other
|
||||
* kinds of data specific to an objects mode.
|
||||
* kinds of data specific to an object's mode.
|
||||
*
|
||||
* The flag signals that the mesh hasn't been modified from the data that generated it,
|
||||
* allowing us to use the object-mode data for drawing.
|
||||
*
|
||||
* While this could be the callers responsibility, keep here since it's
|
||||
* While this could be the caller's responsibility, keep here since it's
|
||||
* highly unlikely we want to create a duplicate and not use it for drawing. */
|
||||
mesh_dst->runtime->is_original_bmesh = false;
|
||||
|
||||
|
|
|
@ -136,10 +136,10 @@ static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset
|
|||
*/
|
||||
static void try_tag_verts_no_face_none(const Mesh &mesh)
|
||||
{
|
||||
if (mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
|
||||
if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
|
||||
return;
|
||||
}
|
||||
if (mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
|
||||
if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
|
||||
return;
|
||||
}
|
||||
mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
|
||||
|
|
|
@ -795,7 +795,7 @@ static void foreach_vertex_of_loose_edge(const SubdivForeachContext *foreach_con
|
|||
static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
|
||||
const int subdiv_v1,
|
||||
const int subdiv_v2,
|
||||
const char crease)
|
||||
const float crease)
|
||||
{
|
||||
/* This is a bit overhead to use atomics in such a simple function called from many threads,
|
||||
* but this allows to save quite measurable amount of memory. */
|
||||
|
@ -805,7 +805,7 @@ static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
|
|||
Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
|
||||
edge->v1 = subdiv_v1;
|
||||
edge->v2 = subdiv_v2;
|
||||
edge->sharpness = BKE_subdiv_crease_to_sharpness_char(crease);
|
||||
edge->sharpness = BKE_subdiv_crease_to_sharpness_f(crease);
|
||||
}
|
||||
|
||||
static void foreach_edge(const SubdivForeachContext *foreach_context,
|
||||
|
@ -821,7 +821,7 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
|
|||
|
||||
if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) {
|
||||
if (!is_loose) {
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, char(255));
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, 1.0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -837,8 +837,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
|
|||
return;
|
||||
}
|
||||
/* Edges without crease are to be ignored as well. */
|
||||
const char crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
|
||||
if (crease == 0) {
|
||||
const float crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
|
||||
if (crease == 0.0f) {
|
||||
return;
|
||||
}
|
||||
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease);
|
||||
|
|
|
@ -2430,29 +2430,7 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
|||
return node_dst;
|
||||
}
|
||||
|
||||
static void for_each_node_group_instance(Main &bmain,
|
||||
const bNodeTree &node_group,
|
||||
const Span<int> tree_types_to_lookup,
|
||||
const FunctionRef<void(bNode &)> func)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeTree *, other_group, &bmain.nodetrees) {
|
||||
if (!tree_types_to_lookup.contains(other_group->type)) {
|
||||
continue;
|
||||
}
|
||||
if (other_group == &node_group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
other_group->ensure_topology_cache();
|
||||
for (bNode *node : other_group->group_nodes()) {
|
||||
if (node->id == &node_group.id) {
|
||||
func(*node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_socket_move_default_value(Main &bmain,
|
||||
void node_socket_move_default_value(Main & /*bmain*/,
|
||||
bNodeTree &tree,
|
||||
bNodeSocket &src,
|
||||
bNodeSocket &dst)
|
||||
|
@ -2490,14 +2468,6 @@ void node_socket_move_default_value(Main &bmain,
|
|||
dst_values.append(&dst_node.id);
|
||||
break;
|
||||
}
|
||||
case NODE_GROUP_INPUT: {
|
||||
for_each_node_group_instance(bmain, tree, {NTREE_GEOMETRY}, [&](bNode &node_group) {
|
||||
bNodeSocket &socket = node_group.input_by_identifier(dst.identifier);
|
||||
Image **tmp_dst_value = &socket.default_value_typed<bNodeSocketValueImage>()->value;
|
||||
dst_values.append(reinterpret_cast<ID **>(tmp_dst_value));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif
|
|||
return 10.0f;
|
||||
}
|
||||
#endif
|
||||
if (!storage->settings.use_creases || storage->cd_edge_crease == nullptr) {
|
||||
if (storage->cd_edge_crease == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
|
||||
|
@ -187,7 +187,7 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
|
|||
static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, int manifold_vertex_index)
|
||||
{
|
||||
ConverterStorage *storage = static_cast<ConverterStorage *>(converter->user_data);
|
||||
if (!storage->settings.use_creases || storage->cd_vertex_crease == nullptr) {
|
||||
if (storage->cd_vertex_crease == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
|
||||
|
@ -214,16 +214,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
|
|||
storage->loop_uv_indices = static_cast<int *>(
|
||||
MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index"));
|
||||
}
|
||||
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
|
||||
storage->polys,
|
||||
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"),
|
||||
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"),
|
||||
storage->corner_verts.data(),
|
||||
mloopuv,
|
||||
num_vert,
|
||||
limit,
|
||||
false,
|
||||
true);
|
||||
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(storage->polys,
|
||||
nullptr,
|
||||
nullptr,
|
||||
storage->corner_verts.data(),
|
||||
mloopuv,
|
||||
num_vert,
|
||||
limit,
|
||||
false,
|
||||
true);
|
||||
/* NOTE: First UV vertex is supposed to be always marked as separate. */
|
||||
storage->num_uv_coordinates = -1;
|
||||
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
|
||||
|
@ -395,10 +394,12 @@ static void init_user_data(OpenSubdiv_Converter *converter,
|
|||
user_data->polys = mesh->polys();
|
||||
user_data->corner_verts = mesh->corner_verts();
|
||||
user_data->corner_edges = mesh->corner_edges();
|
||||
user_data->cd_vertex_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->vdata, CD_CREASE));
|
||||
user_data->cd_edge_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->edata, CD_CREASE));
|
||||
if (settings->use_creases) {
|
||||
user_data->cd_vertex_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->vdata, CD_CREASE));
|
||||
user_data->cd_edge_crease = static_cast<const float *>(
|
||||
CustomData_get_layer(&mesh->edata, CD_CREASE));
|
||||
}
|
||||
user_data->loop_uv_indices = nullptr;
|
||||
initialize_manifold_indices(user_data);
|
||||
converter->user_data = user_data;
|
||||
|
|
|
@ -91,9 +91,3 @@ BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease)
|
|||
{
|
||||
return edge_crease * edge_crease * 10.0f;
|
||||
}
|
||||
|
||||
BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease)
|
||||
{
|
||||
const float edge_crease_f = edge_crease / 255.0f;
|
||||
return BKE_subdiv_crease_to_sharpness_f(edge_crease_f);
|
||||
}
|
||||
|
|
|
@ -1192,11 +1192,19 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
|
|||
BitVector<> &bit_vector = result->runtime->subsurf_optimal_display_edges;
|
||||
bit_vector.clear();
|
||||
bit_vector.resize(subdiv_context.subdiv_display_edges.size());
|
||||
threading::parallel_for_aligned(span.index_range(), 4096, 64, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
bit_vector[i].set(span[i]);
|
||||
}
|
||||
});
|
||||
threading::parallel_for_aligned(
|
||||
span.index_range(), 4096, bits::BitsPerInt, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
bit_vector[i].set(span[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (coarse_mesh->verts_no_face().count == 0) {
|
||||
result->tag_loose_verts_none();
|
||||
}
|
||||
if (coarse_mesh->loose_edges().count == 0) {
|
||||
result->loose_edges_tag_none();
|
||||
}
|
||||
|
||||
if (subdiv->settings.is_simple) {
|
||||
|
|
|
@ -170,12 +170,9 @@ Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtim
|
|||
return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_gpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
else {
|
||||
runtime_data->used_cpu = 2;
|
||||
|
||||
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
runtime_data->used_cpu = 2;
|
||||
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
|
||||
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)
|
||||
|
|
|
@ -625,7 +625,7 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_
|
|||
}
|
||||
const mf::MultiFunction &fn = *this->get_conversion_multi_function(
|
||||
mf::DataType::ForSingle(from_type), mf::DataType::ForSingle(to_type));
|
||||
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})};
|
||||
return {fn::FieldOperation::Create(fn, {std::move(field)})};
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -15,6 +15,14 @@ namespace blender::array_utils {
|
|||
* grain-size.
|
||||
*/
|
||||
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
|
||||
template<typename T>
|
||||
inline void copy(const VArray<T> &src, MutableSpan<T> dst, const int64_t grain_size = 4096)
|
||||
{
|
||||
BLI_assert(src.size() == dst.size());
|
||||
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
|
||||
src.materialize_to_uninitialized(range, dst);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying all values from the `src` array. Threaded based on
|
||||
|
@ -28,6 +36,7 @@ inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size
|
|||
dst.slice(range).copy_from(src.slice(range));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the destination span by copying masked values from the `src` array. Threaded based on
|
||||
* grain-size.
|
||||
|
|
|
@ -323,19 +323,18 @@ void BLI_path_sequence_encode(
|
|||
/**
|
||||
* Remove redundant characters from \a path and optionally make absolute.
|
||||
*
|
||||
* \param relabase: The path this is relative to, or ignored when NULL.
|
||||
* \param path: Can be any input, and this function converts it to a regular full path.
|
||||
* Also removes garbage from directory paths, like `/../` or double slashes etc.
|
||||
*
|
||||
* \note \a path isn't protected for max string names.
|
||||
*/
|
||||
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2);
|
||||
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Cleanup file-path ensuring a trailing slash.
|
||||
*
|
||||
* \note Same as #BLI_path_normalize but adds a trailing slash.
|
||||
*/
|
||||
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen) ATTR_NONNULL(2);
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Make given name safe to be used in paths.
|
||||
|
|
|
@ -112,40 +112,55 @@ void BLI_path_sequence_encode(
|
|||
BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
|
||||
}
|
||||
|
||||
void BLI_path_normalize(const char *relabase, char *path)
|
||||
void BLI_path_normalize(char *path)
|
||||
{
|
||||
const char *path_orig = path;
|
||||
int path_len;
|
||||
|
||||
ptrdiff_t a;
|
||||
char *start, *eind;
|
||||
if (relabase) {
|
||||
BLI_path_abs(path, relabase);
|
||||
}
|
||||
else {
|
||||
if (path[0] == '/' && path[1] == '/') {
|
||||
if (path[2] == '\0') {
|
||||
return; /* Path is `//` - can't clean it. */
|
||||
}
|
||||
path = path + 2; /* Leave the initial `//` untouched. */
|
||||
|
||||
path_len = strlen(path);
|
||||
|
||||
if (path[0] == '/' && path[1] == '/') {
|
||||
path = path + 2; /* Leave the initial `//` untouched. */
|
||||
path_len -= 2;
|
||||
|
||||
/* Strip leading slashes, as they will interfere with the absolute/relative check
|
||||
* (besides being redundant). */
|
||||
int i = 0;
|
||||
while (path[i] == SEP) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
memmove(path, path + i, (path_len - i) + 1);
|
||||
path_len -= i;
|
||||
}
|
||||
BLI_assert(path_len == strlen(path));
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/* Skip to the first slash of the drive or UNC path,
|
||||
* so additional slashes are treated as doubles. */
|
||||
{
|
||||
const int path_unc_len = BLI_path_unc_prefix_len(path);
|
||||
if (path_orig == path) {
|
||||
int path_unc_len = BLI_path_unc_prefix_len(path);
|
||||
if (path_unc_len) {
|
||||
BLI_assert(path_unc_len > 1 && path[path_unc_len - 1] == SEP);
|
||||
path += path_unc_len - 1;
|
||||
path_unc_len -= 1;
|
||||
BLI_assert(path_unc_len > 0 && path[path_unc_len] == SEP);
|
||||
path += path_unc_len;
|
||||
path_len -= path_unc_len;
|
||||
}
|
||||
else if (isalpha(path[0]) && path[1] == ':') {
|
||||
else if (isalpha(path[0]) && (path[1] == ':')) {
|
||||
path += 2;
|
||||
}
|
||||
if (path[0] == '\0') {
|
||||
return;
|
||||
path_len -= 2;
|
||||
}
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* Works on WIN32 as well, because the drive component is skipped. */
|
||||
const bool is_relative = path[0] && (path[0] != SEP);
|
||||
|
||||
/* NOTE(@ideasman42):
|
||||
* `memmove(start, eind, strlen(eind) + 1);`
|
||||
* is the same as
|
||||
|
@ -157,7 +172,6 @@ void BLI_path_normalize(const char *relabase, char *path)
|
|||
* - `/./` -> `/`.
|
||||
* - `//` -> `/`.
|
||||
* Performed until no more replacements can be made. */
|
||||
int path_len = strlen(path);
|
||||
if (path_len > 1) {
|
||||
for (int i = path_len - 1; i > 0; i--) {
|
||||
/* Calculate the redundant slash span (if any). */
|
||||
|
@ -187,21 +201,50 @@ void BLI_path_normalize(const char *relabase, char *path)
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove redundant `./` prefix, while it could be kept, it confuses the loop below. */
|
||||
if (is_relative) {
|
||||
if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
|
||||
memmove(path, path + 2, (path_len - 2) + 1);
|
||||
path_len -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
const ptrdiff_t a_start = is_relative ? 0 : 1;
|
||||
start = path;
|
||||
while ((start = strstr(start, SEP_STR ".." SEP_STR))) {
|
||||
a = start - path - 1;
|
||||
if (a > 0) {
|
||||
while ((start = strstr(start, SEP_STR ".."))) {
|
||||
if (!ELEM(start[3], SEP, '\0')) {
|
||||
start += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
a = (start - path) - 1;
|
||||
if (a >= a_start) {
|
||||
/* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */
|
||||
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep last "/". */
|
||||
const size_t eind_len = path_len - (eind - path);
|
||||
BLI_assert(eind_len == strlen(eind));
|
||||
while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
|
||||
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep the char after. */
|
||||
while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
|
||||
a--;
|
||||
}
|
||||
start = path + a;
|
||||
memmove(start, eind, eind_len + 1);
|
||||
path_len -= (eind - start);
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
|
||||
if (is_relative && (a == 0) && *eind) {
|
||||
/* When the path does not start with a slash, don't copy the first `/` to the destination
|
||||
* as it will make a relative path into an absolute path. */
|
||||
eind += 1;
|
||||
}
|
||||
const size_t eind_len = path_len - (eind - path);
|
||||
BLI_assert(eind_len == strlen(eind));
|
||||
|
||||
/* Only remove the parent if it's not also a `..`. */
|
||||
if (is_relative && STRPREFIX(path + ((path[a] == SEP) ? a + 1 : a), ".." SEP_STR)) {
|
||||
start += 3 /* `strlen("/..")` */;
|
||||
}
|
||||
else {
|
||||
start = path + a;
|
||||
BLI_assert(start < eind);
|
||||
memmove(start, eind, eind_len + 1);
|
||||
path_len -= (eind - start);
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
BLI_assert(!is_relative || (path[0] != SEP));
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Support for odd paths: eg `/../home/me` --> `/home/me`
|
||||
|
@ -218,20 +261,27 @@ void BLI_path_normalize(const char *relabase, char *path)
|
|||
memmove(start, eind, eind_len + 1);
|
||||
path_len -= 3;
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
BLI_assert(!is_relative || (path[0] != SEP));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_relative && path_len == 0 && (path == path_orig)) {
|
||||
path[0] = '.';
|
||||
path[1] = '\0';
|
||||
path_len += 1;
|
||||
}
|
||||
|
||||
BLI_assert(strlen(path) == path_len);
|
||||
}
|
||||
|
||||
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen)
|
||||
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen)
|
||||
{
|
||||
/* Would just create an unexpected "/" path, just early exit entirely. */
|
||||
if (dir[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_path_normalize(relabase, dir);
|
||||
BLI_path_normalize(dir);
|
||||
BLI_path_slash_ensure(dir, dir_maxlen);
|
||||
}
|
||||
|
||||
|
@ -523,8 +573,8 @@ void BLI_path_rel(char *file, const char *relfile)
|
|||
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
|
||||
|
||||
/* Remove `/./` which confuse the following slash counting. */
|
||||
BLI_path_normalize(NULL, file);
|
||||
BLI_path_normalize(NULL, temp);
|
||||
BLI_path_normalize(file);
|
||||
BLI_path_normalize(temp);
|
||||
|
||||
/* The last slash in the file indicates where the path part ends. */
|
||||
lslash = BLI_path_slash_rfind(temp);
|
||||
|
@ -925,7 +975,7 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
BLI_strncpy(base, basepath, sizeof(base));
|
||||
|
||||
/* File component is ignored, so don't bother with the trailing slash. */
|
||||
BLI_path_normalize(NULL, base);
|
||||
BLI_path_normalize(base);
|
||||
lslash = BLI_path_slash_rfind(base);
|
||||
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
|
||||
|
||||
|
@ -957,7 +1007,7 @@ bool BLI_path_abs(char *path, const char *basepath)
|
|||
#endif
|
||||
|
||||
/* Ensure this is after correcting for path switch. */
|
||||
BLI_path_normalize(NULL, path);
|
||||
BLI_path_normalize(path);
|
||||
|
||||
return wasrelative;
|
||||
}
|
||||
|
@ -1657,8 +1707,8 @@ bool BLI_path_contains(const char *container_path, const char *containee_path)
|
|||
BLI_path_slash_native(container_native);
|
||||
BLI_path_slash_native(containee_native);
|
||||
|
||||
BLI_path_normalize(NULL, container_native);
|
||||
BLI_path_normalize(NULL, containee_native);
|
||||
BLI_path_normalize(container_native);
|
||||
BLI_path_normalize(containee_native);
|
||||
|
||||
#ifdef WIN32
|
||||
BLI_str_tolower_ascii(container_native, PATH_MAX);
|
||||
|
@ -1761,8 +1811,8 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
|||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
||||
BLI_path_normalize(NULL, norm_p1);
|
||||
BLI_path_normalize(NULL, norm_p2);
|
||||
BLI_path_normalize(norm_p1);
|
||||
BLI_path_normalize(norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2);
|
||||
}
|
||||
|
|
|
@ -42,26 +42,38 @@ static char *str_replace_char_strdup(const char *str, char src, char dst)
|
|||
/** \name Tests for: #BLI_path_normalize
|
||||
* \{ */
|
||||
|
||||
#define NORMALIZE_WITH_BASEDIR(input, input_base, output_expect) \
|
||||
#define NORMALIZE(input, output_expect) \
|
||||
{ \
|
||||
char path[FILE_MAX] = input; \
|
||||
const char *input_base_test = input_base; \
|
||||
if (SEP == '\\') { \
|
||||
str_replace_char_with_relative_exception(path, '/', '\\'); \
|
||||
input_base_test = str_replace_char_strdup(input_base_test, '/', '\\'); \
|
||||
} \
|
||||
BLI_path_normalize(input_base_test, path); \
|
||||
BLI_path_normalize(path); \
|
||||
if (SEP == '\\') { \
|
||||
BLI_str_replace_char(path, '\\', '/'); \
|
||||
if (input_base_test) { \
|
||||
free((void *)input_base_test); \
|
||||
} \
|
||||
} \
|
||||
EXPECT_STREQ(path, output_expect); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define NORMALIZE(input, output_expect) NORMALIZE_WITH_BASEDIR(input, nullptr, output_expect)
|
||||
/* #BLI_path_normalize: do nothing. */
|
||||
TEST(path_util, Normalize_Nop)
|
||||
{
|
||||
NORMALIZE(".", ".");
|
||||
NORMALIZE("./", "./");
|
||||
NORMALIZE("/", "/");
|
||||
NORMALIZE("//", "//");
|
||||
NORMALIZE("//a", "//a");
|
||||
}
|
||||
|
||||
TEST(path_util, Normalize_NopRelative)
|
||||
{
|
||||
NORMALIZE("..", "..");
|
||||
NORMALIZE("../", "../");
|
||||
NORMALIZE("../", "../");
|
||||
NORMALIZE("../..", "../..");
|
||||
NORMALIZE("../../", "../../");
|
||||
}
|
||||
|
||||
/* #BLI_path_normalize: "/./" -> "/" */
|
||||
TEST(path_util, Normalize_Dot)
|
||||
|
@ -83,17 +95,16 @@ TEST(path_util, Normalize_DoubleSlash)
|
|||
NORMALIZE("//", "//"); /* Exception, double forward slash. */
|
||||
NORMALIZE(".//", "./");
|
||||
NORMALIZE("a////", "a/");
|
||||
NORMALIZE("./a////", "./a/");
|
||||
NORMALIZE("./a////", "a/");
|
||||
}
|
||||
/* #BLI_path_normalize: "foo/bar/../" -> "foo/" */
|
||||
TEST(path_util, Normalize_Parent)
|
||||
{
|
||||
NORMALIZE("/a/b/c/../../../", "/");
|
||||
NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/");
|
||||
NORMALIZE_WITH_BASEDIR("//../", "/a/b/c/", "/a/b/");
|
||||
}
|
||||
/* #BLI_path_normalize: with too many "/../", match Python's behavior. */
|
||||
TEST(path_util, Normalize_Unbalanced)
|
||||
TEST(path_util, Normalize_UnbalancedAbsolute)
|
||||
{
|
||||
NORMALIZE("/../", "/");
|
||||
NORMALIZE("/../a", "/a");
|
||||
|
@ -102,7 +113,39 @@ TEST(path_util, Normalize_Unbalanced)
|
|||
NORMALIZE("/a/b/c/../../../d", "/d");
|
||||
}
|
||||
|
||||
#undef NORMALIZE_WITH_BASEDIR
|
||||
/* #BLI_path_normalize: with relative paths that result in leading "../". */
|
||||
TEST(path_util, Normalize_UnbalancedRelative)
|
||||
{
|
||||
NORMALIZE("./a/b/c/../../../", ".");
|
||||
NORMALIZE("a/b/c/../../../", ".");
|
||||
NORMALIZE("//a/b/c/../../../", "//");
|
||||
|
||||
NORMALIZE("./a/../../../", "../../");
|
||||
NORMALIZE("a/../../../", "../../");
|
||||
|
||||
NORMALIZE("///a/../../../", "//../../");
|
||||
NORMALIZE("//./a/../../../", "//../../");
|
||||
|
||||
NORMALIZE("../a/../../../", "../../../");
|
||||
NORMALIZE("a/b/../c/../../d/../../../e/../../../../f", "../../../../../f");
|
||||
NORMALIZE(".../.../a/.../b/../c/../../d/../../../e/../../../.../../f", "../f");
|
||||
}
|
||||
|
||||
TEST(path_util, Normalize_UnbalancedRelativeTrailing)
|
||||
{
|
||||
NORMALIZE("./a/b/c/../../..", ".");
|
||||
NORMALIZE("a/b/c/../../..", ".");
|
||||
NORMALIZE("//a/b/c/../../..", "//");
|
||||
|
||||
NORMALIZE("./a/../../..", "../..");
|
||||
NORMALIZE("a/../../..", "../..");
|
||||
|
||||
NORMALIZE("///a/../../..", "//../..");
|
||||
NORMALIZE("//./a/../../..", "//../..");
|
||||
|
||||
NORMALIZE("../a/../../..", "../../..");
|
||||
}
|
||||
|
||||
#undef NORMALIZE
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -516,7 +516,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
|
|||
char name1[FILE_MAX];
|
||||
|
||||
BLI_strncpy(name1, filepath, sizeof(name1));
|
||||
BLI_path_normalize(relabase, name1);
|
||||
BLI_path_abs(name1, relabase);
|
||||
BLI_path_normalize(name1);
|
||||
|
||||
// printf("blo_find_main: relabase %s\n", relabase);
|
||||
// printf("blo_find_main: original in %s\n", filepath);
|
||||
|
@ -2695,7 +2696,8 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
|
|||
|
||||
/* Make sure we have full path in lib->filepath_abs */
|
||||
BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath));
|
||||
BLI_path_normalize(fd->relabase, lib->filepath_abs);
|
||||
BLI_path_abs(lib->filepath_abs, fd->relabase);
|
||||
BLI_path_normalize(lib->filepath_abs);
|
||||
|
||||
// printf("direct_link_library: filepath %s\n", lib->filepath);
|
||||
// printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs);
|
||||
|
@ -3115,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
|
||||
|
|
|
@ -1474,13 +1474,13 @@ bool BLO_write_file(Main *mainvar,
|
|||
/* Normalize the paths in case there is some subtle difference (so they can be compared). */
|
||||
if (relbase_valid) {
|
||||
BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
|
||||
BLI_path_normalize(nullptr, dir_src);
|
||||
BLI_path_normalize(dir_src);
|
||||
}
|
||||
else {
|
||||
dir_src[0] = '\0';
|
||||
}
|
||||
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
|
||||
BLI_path_normalize(nullptr, dir_dst);
|
||||
BLI_path_normalize(dir_dst);
|
||||
|
||||
/* Only for relative, not relative-all, as this means making existing paths relative. */
|
||||
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* -------- */
|
||||
|
||||
/**
|
||||
|
|
|
@ -198,11 +198,12 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
|
|||
}
|
||||
else if (file->redirection_path) {
|
||||
BLI_strncpy(params->dir, file->redirection_path, sizeof(params->dir));
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir));
|
||||
BLI_path_slash_ensure(params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
}
|
||||
else {
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath);
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1096,8 @@ static int bookmark_select_exec(bContext *C, wmOperator *op)
|
|||
|
||||
RNA_property_string_get(op->ptr, prop, entry);
|
||||
BLI_strncpy(params->dir, entry, sizeof(params->dir));
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
ED_file_change_dir(C);
|
||||
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
|
||||
|
@ -2058,7 +2060,8 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
|
|||
BLI_path_parent_dir(params->dir);
|
||||
}
|
||||
else {
|
||||
BLI_path_normalize(BKE_main_blendfile_path(bmain), params->dir);
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize(params->dir);
|
||||
BLI_path_append_dir(params->dir, sizeof(params->dir), file->relpath);
|
||||
}
|
||||
ED_file_change_dir(C);
|
||||
|
@ -2222,7 +2225,8 @@ static int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
|
|||
|
||||
if (params) {
|
||||
if (BLI_path_parent_dir(params->dir)) {
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
ED_file_change_dir(C);
|
||||
if (params->recursion_level > 1) {
|
||||
/* Disable 'dirtree' recursion when going up in tree. */
|
||||
|
@ -2805,7 +2809,8 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
|
|||
}
|
||||
}
|
||||
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
|
||||
if (filelist_is_dir(sfile->files, params->dir)) {
|
||||
if (!STREQ(params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */
|
||||
|
@ -2892,7 +2897,8 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg
|
|||
|
||||
/* if directory, open it and empty filename field */
|
||||
if (filelist_is_dir(sfile->files, filepath)) {
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
|
||||
BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
|
||||
BLI_path_normalize_dir(filepath, sizeof(filepath));
|
||||
BLI_strncpy(params->dir, filepath, sizeof(params->dir));
|
||||
params->file[0] = '\0';
|
||||
ED_file_change_dir(C);
|
||||
|
|
|
@ -1984,7 +1984,8 @@ void filelist_setdir(FileList *filelist, char *r_dir)
|
|||
const bool allow_invalid = filelist->asset_library_ref != nullptr;
|
||||
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
|
||||
|
||||
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir, FILE_MAX_LIBEXTRA);
|
||||
BLI_path_abs(r_dir, BKE_main_blendfile_path_from_global());
|
||||
BLI_path_normalize_dir(r_dir, FILE_MAX_LIBEXTRA);
|
||||
const bool is_valid_path = filelist->check_dir_fn(filelist, r_dir, !allow_invalid);
|
||||
BLI_assert(is_valid_path || allow_invalid);
|
||||
UNUSED_VARS_NDEBUG(is_valid_path);
|
||||
|
@ -2916,7 +2917,7 @@ struct TodoDir {
|
|||
|
||||
struct FileListReadJob {
|
||||
ThreadMutex lock;
|
||||
char main_name[FILE_MAX];
|
||||
char main_filepath[FILE_MAX];
|
||||
Main *current_main;
|
||||
FileList *filelist;
|
||||
|
||||
|
@ -2981,7 +2982,7 @@ static int filelist_readjob_list_dir(FileListReadJob *job_params,
|
|||
ListBase *entries,
|
||||
const char *filter_glob,
|
||||
const bool do_lib,
|
||||
const char *main_name,
|
||||
const char *main_filepath,
|
||||
const bool skip_currpar)
|
||||
{
|
||||
direntry *files;
|
||||
|
@ -3045,7 +3046,7 @@ static int filelist_readjob_list_dir(FileListReadJob *job_params,
|
|||
/* If we are considering .blend files as libraries, promote them to directory status. */
|
||||
entry->typeflag = FILE_TYPE_BLENDER;
|
||||
/* prevent current file being used as acceptable dir */
|
||||
if (BLI_path_cmp(main_name, target) != 0) {
|
||||
if (BLI_path_cmp(main_filepath, target) != 0) {
|
||||
entry->typeflag |= FILE_TYPE_DIR;
|
||||
}
|
||||
}
|
||||
|
@ -3586,7 +3587,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
|
|||
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
|
||||
BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
|
||||
|
||||
BLI_path_normalize_dir(job_params->main_name, dir, sizeof(dir));
|
||||
BLI_path_abs(dir, job_params->main_filepath);
|
||||
BLI_path_normalize_dir(dir, sizeof(dir));
|
||||
td_dir->dir = BLI_strdup(dir);
|
||||
|
||||
/* Init the file indexer. */
|
||||
|
@ -3617,7 +3619,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
|
|||
* Note that in the end, this means we 'cache' valid relative subdir once here,
|
||||
* this is actually better. */
|
||||
BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir));
|
||||
BLI_path_normalize_dir(root, rel_subdir, sizeof(rel_subdir));
|
||||
BLI_path_abs(rel_subdir, root);
|
||||
BLI_path_normalize_dir(rel_subdir, sizeof(rel_subdir));
|
||||
BLI_path_rel(rel_subdir, root);
|
||||
|
||||
/* Update the current relative base path within the filelist root. */
|
||||
|
@ -3649,8 +3652,13 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
|
|||
}
|
||||
|
||||
if (!is_lib && BLI_is_dir(subdir)) {
|
||||
entries_num = filelist_readjob_list_dir(
|
||||
job_params, subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
|
||||
entries_num = filelist_readjob_list_dir(job_params,
|
||||
subdir,
|
||||
&entries,
|
||||
filter_glob,
|
||||
do_lib,
|
||||
job_params->main_filepath,
|
||||
skip_currpar);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) {
|
||||
|
@ -3663,7 +3671,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
|
|||
/* We have a directory we want to list, add it to todo list!
|
||||
* Using #BLI_path_join works but isn't needed as `root` has a trailing slash. */
|
||||
BLI_string_join(dir, sizeof(dir), root, entry->relpath);
|
||||
BLI_path_normalize_dir(job_params->main_name, dir, sizeof(dir));
|
||||
BLI_path_abs(dir, job_params->main_filepath);
|
||||
BLI_path_normalize_dir(dir, sizeof(dir));
|
||||
td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
|
||||
td_dir->level = recursion_level + 1;
|
||||
td_dir->dir = BLI_strdup(dir);
|
||||
|
@ -4088,7 +4097,7 @@ void filelist_readjob_start(FileList *filelist, const int space_notifier, const
|
|||
flrj = MEM_cnew<FileListReadJob>(__func__);
|
||||
flrj->filelist = filelist;
|
||||
flrj->current_main = bmain;
|
||||
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
|
||||
BLI_strncpy(flrj->main_filepath, BKE_main_blendfile_path(bmain), sizeof(flrj->main_filepath));
|
||||
if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) {
|
||||
flrj->only_main_data = true;
|
||||
}
|
||||
|
|
|
@ -203,8 +203,8 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
|
|||
}
|
||||
|
||||
if (params->dir[0]) {
|
||||
BLI_path_normalize_dir(blendfile_path, params->dir, sizeof(params->dir));
|
||||
BLI_path_abs(params->dir, blendfile_path);
|
||||
BLI_path_normalize_dir(params->dir, sizeof(params->dir));
|
||||
}
|
||||
|
||||
params->flag = 0;
|
||||
|
|
|
@ -571,7 +571,7 @@ static Mesh *subdivide_edit_mesh(const Object *object,
|
|||
mesh_settings.resolution = (1 << smd->levels) + 1;
|
||||
mesh_settings.use_optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges);
|
||||
|
||||
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &settings, me_from_em);
|
||||
Subdiv *subdiv = BKE_subdiv_new_from_mesh(&settings, me_from_em);
|
||||
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me_from_em);
|
||||
BKE_id_free(nullptr, me_from_em);
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
|
|
@ -331,7 +331,7 @@ int Controller::LoadMesh(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph
|
|||
soc string basename((const char *)qfi.fileName().toAscii().data());
|
||||
char cleaned[FILE_MAX];
|
||||
BLI_strncpy(cleaned, iFileName, FILE_MAX);
|
||||
BLI_path_normalize(NULL, cleaned);
|
||||
BLI_path_normalize(cleaned);
|
||||
string basename = string(cleaned);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ void getPathName(const string &path, const string &base, vector<string> &pathnam
|
|||
dir = path.substr(pos, sep - pos);
|
||||
|
||||
BLI_strncpy(cleaned, dir.c_str(), FILE_MAX);
|
||||
BLI_path_normalize(nullptr, cleaned);
|
||||
BLI_path_normalize(cleaned);
|
||||
res = string(cleaned);
|
||||
|
||||
if (!base.empty()) {
|
||||
|
|
|
@ -522,7 +522,7 @@ Field<bool> invert_boolean_field(const Field<bool> &field)
|
|||
{
|
||||
static auto not_fn = mf::build::SI1_SO<bool, bool>(
|
||||
"Not", [](bool a) { return !a; }, mf::build::exec_presets::AllSpanOrSingle());
|
||||
auto not_op = std::make_shared<FieldOperation>(FieldOperation(not_fn, {field}));
|
||||
auto not_op = FieldOperation::Create(not_fn, {field});
|
||||
return Field<bool>(not_op);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,8 @@ namespace blender::fn::tests {
|
|||
|
||||
TEST(field, ConstantFunction)
|
||||
{
|
||||
/* TODO: Figure out how to not use another "FieldOperation(" inside of std::make_shared. */
|
||||
GField constant_field{std::make_shared<FieldOperation>(
|
||||
FieldOperation(std::make_unique<mf::CustomMF_Constant<int>>(10), {})),
|
||||
0};
|
||||
GField constant_field{
|
||||
FieldOperation::Create(std::make_unique<mf::CustomMF_Constant<int>>(10), {}), 0};
|
||||
|
||||
Array<int> result(4);
|
||||
|
||||
|
@ -103,8 +101,7 @@ TEST(field, InputAndFunction)
|
|||
GField index_field{std::make_shared<IndexFieldInput>()};
|
||||
|
||||
auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
|
||||
GField output_field{
|
||||
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
|
||||
GField output_field{FieldOperation::Create(add_fn, {index_field, index_field}), 0};
|
||||
|
||||
Array<int> result(10);
|
||||
|
||||
|
@ -126,11 +123,10 @@ TEST(field, TwoFunctions)
|
|||
GField index_field{std::make_shared<IndexFieldInput>()};
|
||||
|
||||
auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
|
||||
GField add_field{
|
||||
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
|
||||
GField add_field{FieldOperation::Create(add_fn, {index_field, index_field}), 0};
|
||||
|
||||
auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
|
||||
GField result_field{std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {add_field})), 0};
|
||||
GField result_field{FieldOperation::Create(add_10_fn, {add_field}), 0};
|
||||
|
||||
Array<int> result(10);
|
||||
|
||||
|
@ -181,8 +177,8 @@ TEST(field, FunctionTwoOutputs)
|
|||
GField index_field_1{std::make_shared<IndexFieldInput>()};
|
||||
GField index_field_2{std::make_shared<IndexFieldInput>()};
|
||||
|
||||
std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>(
|
||||
FieldOperation(std::make_unique<TwoOutputFunction>(), {index_field_1, index_field_2}));
|
||||
std::shared_ptr<FieldOperation> fn = FieldOperation::Create(
|
||||
std::make_unique<TwoOutputFunction>(), {index_field_1, index_field_2});
|
||||
|
||||
GField result_field_1{fn, 0};
|
||||
GField result_field_2{fn, 1};
|
||||
|
@ -212,8 +208,8 @@ TEST(field, TwoFunctionsTwoOutputs)
|
|||
{
|
||||
GField index_field{std::make_shared<IndexFieldInput>()};
|
||||
|
||||
std::shared_ptr<FieldOperation> fn = std::make_shared<FieldOperation>(
|
||||
FieldOperation(std::make_unique<TwoOutputFunction>(), {index_field, index_field}));
|
||||
std::shared_ptr<FieldOperation> fn = FieldOperation::Create(
|
||||
std::make_unique<TwoOutputFunction>(), {index_field, index_field});
|
||||
|
||||
Array<int64_t> mask_indices = {2, 4, 6, 8};
|
||||
IndexMask mask = mask_indices.as_span();
|
||||
|
@ -222,8 +218,7 @@ TEST(field, TwoFunctionsTwoOutputs)
|
|||
Field<int> intermediate_field{fn, 1};
|
||||
|
||||
auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
|
||||
Field<int> result_field_2{
|
||||
std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {intermediate_field})), 0};
|
||||
Field<int> result_field_2{FieldOperation::Create(add_10_fn, {intermediate_field}), 0};
|
||||
|
||||
FieldContext field_context;
|
||||
FieldEvaluator field_evaluator{field_context, &mask};
|
||||
|
@ -245,8 +240,8 @@ TEST(field, TwoFunctionsTwoOutputs)
|
|||
|
||||
TEST(field, SameFieldTwice)
|
||||
{
|
||||
GField constant_field{
|
||||
std::make_shared<FieldOperation>(std::make_unique<mf::CustomMF_Constant<int>>(10)), 0};
|
||||
GField constant_field{FieldOperation::Create(std::make_unique<mf::CustomMF_Constant<int>>(10)),
|
||||
0};
|
||||
|
||||
FieldContext field_context;
|
||||
IndexMask mask{IndexRange(2)};
|
||||
|
@ -266,7 +261,7 @@ TEST(field, SameFieldTwice)
|
|||
TEST(field, IgnoredOutput)
|
||||
{
|
||||
static mf::tests::OptionalOutputsFunction fn;
|
||||
Field<int> field{std::make_shared<FieldOperation>(fn), 0};
|
||||
Field<int> field{FieldOperation::Create(fn), 0};
|
||||
|
||||
FieldContext field_context;
|
||||
FieldEvaluator field_evaluator{field_context, 10};
|
||||
|
|
|
@ -21,10 +21,7 @@ static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
|
|||
"Clamp Above One",
|
||||
[](int value) { return std::max(1, value); },
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
auto clamp_op = std::make_shared<fn::FieldOperation>(
|
||||
fn::FieldOperation(max_one_fn, {count_field}));
|
||||
|
||||
return fn::Field<int>(std::move(clamp_op));
|
||||
return fn::Field<int>(fn::FieldOperation::Create(max_one_fn, {count_field}));
|
||||
}
|
||||
|
||||
static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field)
|
||||
|
@ -39,9 +36,9 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
|
|||
},
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
|
||||
auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation(
|
||||
auto get_count_op = fn::FieldOperation::Create(
|
||||
get_count_fn,
|
||||
{fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field}));
|
||||
{fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field});
|
||||
|
||||
return fn::Field<int>(std::move(get_count_op));
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -93,7 +93,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
|
|||
/* make absolute source path */
|
||||
BLI_strncpy(source_path, image->filepath, sizeof(source_path));
|
||||
BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id));
|
||||
BLI_path_normalize(nullptr, source_path);
|
||||
BLI_path_normalize(source_path);
|
||||
|
||||
if (use_copies) {
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ std::string path_reference(StringRefNull filepath,
|
|||
char filepath_abs[PATH_MAX];
|
||||
BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX);
|
||||
BLI_path_abs(filepath_abs, base_src.c_str());
|
||||
BLI_path_normalize(nullptr, filepath_abs);
|
||||
BLI_path_normalize(filepath_abs);
|
||||
|
||||
/* Figure out final mode to be used. */
|
||||
if (mode == PATH_REFERENCE_MATCH) {
|
||||
|
|
|
@ -82,7 +82,7 @@ static std::string copy_asset_to_directory(const char *src_path,
|
|||
|
||||
char dest_file_path[FILE_MAX];
|
||||
BLI_path_join(dest_file_path, sizeof(dest_file_path), dest_dir_path, base_name.c_str());
|
||||
BLI_path_normalize(NULL, dest_file_path);
|
||||
BLI_path_normalize(dest_file_path);
|
||||
|
||||
if (name_collision_mode == USD_TEX_NAME_COLLISION_USE_EXISTING && BLI_is_file(dest_file_path)) {
|
||||
return dest_file_path;
|
||||
|
@ -278,7 +278,8 @@ std::string import_asset(const char *src,
|
|||
}
|
||||
}
|
||||
|
||||
BLI_path_normalize(basepath, dest_dir_path);
|
||||
BLI_path_abs(dest_dir_path, basepath);
|
||||
BLI_path_normalize(dest_dir_path);
|
||||
|
||||
if (!BLI_dir_create_recursive(dest_dir_path)) {
|
||||
WM_reportf(
|
||||
|
|
|
@ -444,7 +444,7 @@ static void get_absolute_path(Image *ima, char *r_path)
|
|||
/* Make absolute source path. */
|
||||
BLI_strncpy(r_path, ima->filepath, FILE_MAX);
|
||||
BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
|
||||
BLI_path_normalize(nullptr, r_path);
|
||||
BLI_path_normalize(r_path);
|
||||
}
|
||||
|
||||
static pxr::TfToken get_node_tex_image_color_space(bNode *node)
|
||||
|
|
|
@ -666,7 +666,7 @@ void MTLWriter::write_materials(const char *blen_filepath,
|
|||
char blen_filedir[PATH_MAX];
|
||||
BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX);
|
||||
BLI_path_slash_native(blen_filedir);
|
||||
BLI_path_normalize(nullptr, blen_filedir);
|
||||
BLI_path_normalize(blen_filedir);
|
||||
|
||||
std::sort(mtlmaterials_.begin(),
|
||||
mtlmaterials_.end(),
|
||||
|
|
|
@ -292,7 +292,7 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
|
|||
BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX);
|
||||
}
|
||||
BLI_path_slash_native(dest_dir);
|
||||
BLI_path_normalize(nullptr, dest_dir);
|
||||
BLI_path_normalize(dest_dir);
|
||||
mtl_writer->write_materials(export_params.blen_filepath,
|
||||
export_params.path_mode,
|
||||
dest_dir,
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -1343,9 +1343,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
"Scale",
|
||||
[](const float3 &offset, const float scale) { return offset * scale; },
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>(
|
||||
FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)}));
|
||||
const Field<float3> final_offset{std::move(multiply_op)};
|
||||
const Field<float3> final_offset{
|
||||
FieldOperation::Create(multiply_fn, {std::move(offset_field), std::move(scale_field)})};
|
||||
|
||||
AttributeOutputs attribute_outputs;
|
||||
attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top");
|
||||
|
@ -1377,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
|
||||
|
|
|
@ -415,8 +415,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
|
||||
Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
|
||||
|
||||
auto image_op = std::make_shared<FieldOperation>(
|
||||
FieldOperation(std::move(image_fn), {std::move(vector_field)}));
|
||||
auto image_op = FieldOperation::Create(std::move(image_fn), {std::move(vector_field)});
|
||||
|
||||
params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
|
||||
params.set_output("Alpha", Field<float>(image_op, 1));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,14 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b)
|
|||
b.add_output<decl::Geometry>(N_("Mesh")).propagate_all();
|
||||
}
|
||||
|
||||
static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
|
||||
static Mesh *simple_subdivide_mesh(const Mesh &mesh, const int level)
|
||||
{
|
||||
if (!geometry_set.has_mesh()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
|
||||
|
||||
/* Initialize mesh settings. */
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
mesh_settings.resolution = (1 << level) + 1;
|
||||
|
@ -42,43 +36,38 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev
|
|||
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(0);
|
||||
|
||||
/* Apply subdivision from mesh. */
|
||||
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
|
||||
|
||||
/* In case of bad topology, skip to input mesh. */
|
||||
if (subdiv == nullptr) {
|
||||
return;
|
||||
Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, &mesh);
|
||||
if (!subdiv) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
|
||||
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace(mesh_out);
|
||||
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
|
||||
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
|
||||
|
||||
#ifndef WITH_OPENSUBDIV
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
/* See CCGSUBSURF_LEVEL_MAX for max limit. */
|
||||
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
|
||||
|
||||
if (subdiv_level == 0) {
|
||||
const int level = clamp_i(params.extract_input<int>("Level"), 0, 11);
|
||||
if (level == 0) {
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
return;
|
||||
}
|
||||
|
||||
geometry_set.modify_geometry_sets(
|
||||
[&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
|
||||
|
||||
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
|
||||
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
|
||||
geometry_set.replace_mesh(simple_subdivide_mesh(*mesh, level));
|
||||
}
|
||||
});
|
||||
#else
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
|
||||
#endif
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
|
|||
}
|
||||
|
||||
static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
|
||||
Field<float3> &position_field,
|
||||
Field<float> &radius_field,
|
||||
Field<bool> &selection_field,
|
||||
const Field<float3> &position_field,
|
||||
const Field<float> &radius_field,
|
||||
const Field<bool> &selection_field,
|
||||
const eAttrDomain domain,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
{
|
||||
|
@ -151,9 +151,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
__func__,
|
||||
[](float value) { return std::max(0.0f, value); },
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
auto max_zero_op = std::make_shared<FieldOperation>(
|
||||
FieldOperation(max_zero_fn, {std::move(radius)}));
|
||||
Field<float> positive_radius(std::move(max_zero_op), 0);
|
||||
const Field<float> positive_radius(FieldOperation::Create(max_zero_fn, {std::move(radius)}), 0);
|
||||
|
||||
const NodeGeometryMeshToPoints &storage = node_storage(params.node());
|
||||
const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
|
||||
|
|
|
@ -198,10 +198,10 @@ class ProximityFunction : public mf::MultiFunction {
|
|||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
|
||||
geometry_set_target.ensure_owns_direct_data();
|
||||
GeometrySet target = params.extract_input<GeometrySet>("Target");
|
||||
target.ensure_owns_direct_data();
|
||||
|
||||
if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
|
||||
if (!target.has_mesh() && !target.has_pointcloud()) {
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
@ -210,9 +210,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
|
||||
|
||||
auto proximity_fn = std::make_unique<ProximityFunction>(
|
||||
std::move(geometry_set_target), GeometryNodeProximityTargetType(storage.target_element));
|
||||
auto proximity_op = std::make_shared<FieldOperation>(
|
||||
FieldOperation(std::move(proximity_fn), {std::move(position_field)}));
|
||||
std::move(target), GeometryNodeProximityTargetType(storage.target_element));
|
||||
auto proximity_op = FieldOperation::Create(std::move(proximity_fn), {std::move(position_field)});
|
||||
|
||||
params.set_output("Position", Field<float3>(proximity_op, 0));
|
||||
params.set_output("Distance", Field<float>(proximity_op, 1));
|
||||
|
|
|
@ -407,9 +407,9 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
Field<float> length_field = params.extract_input<Field<float>>("Ray Length");
|
||||
|
||||
auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping);
|
||||
auto op = std::make_shared<FieldOperation>(FieldOperation(
|
||||
auto op = FieldOperation::Create(
|
||||
std::move(fn),
|
||||
{std::move(position_field), std::move(direction_field), std::move(length_field)}));
|
||||
{std::move(position_field), std::move(direction_field), std::move(length_field)});
|
||||
|
||||
params.set_output("Is Hit", Field<bool>(op, 0));
|
||||
params.set_output("Hit Position", Field<float3>(op, 1));
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_mesh.hh"
|
||||
|
@ -54,136 +54,136 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
|
|||
}
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
static void materialize_and_clamp_creases(const VArray<float> &crease_varray,
|
||||
MutableSpan<float> creases)
|
||||
|
||||
static void write_vert_creases(Mesh &mesh, const VArray<float> &creases)
|
||||
{
|
||||
threading::parallel_for(creases.index_range(), 1024, [&](IndexRange range) {
|
||||
crease_varray.materialize(range, creases);
|
||||
for (const int i : range) {
|
||||
creases[i] = std::clamp(creases[i], 0.0f, 1.0f);
|
||||
}
|
||||
});
|
||||
CustomData_free_layers(&mesh.vdata, CD_CREASE, mesh.totvert);
|
||||
float *layer = static_cast<float *>(
|
||||
CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, mesh.totvert));
|
||||
array_utils::copy(creases, {layer, mesh.totvert});
|
||||
}
|
||||
|
||||
static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
|
||||
static void write_edge_creases(Mesh &mesh, const VArray<float> &creases)
|
||||
{
|
||||
float *crease;
|
||||
if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) {
|
||||
crease = static_cast<float *>(
|
||||
CustomData_get_layer_for_write(&mesh.vdata, CD_CREASE, mesh.totvert));
|
||||
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
attributes.remove("crease");
|
||||
attributes.add<float>("crease", ATTR_DOMAIN_EDGE, bke::AttributeInitVArray(creases));
|
||||
}
|
||||
|
||||
static bool varray_is_single_zero(const VArray<float> &varray)
|
||||
{
|
||||
if (const std::optional<float> value = varray.get_if_single()) {
|
||||
return *value == 0.0f;
|
||||
}
|
||||
else {
|
||||
crease = static_cast<float *>(
|
||||
CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, mesh.totvert));
|
||||
return false;
|
||||
}
|
||||
|
||||
static fn::Field<float> clamp_crease(fn::Field<float> crease_field)
|
||||
{
|
||||
static auto clamp_fn = mf::build::SI1_SO<float, float>(
|
||||
"Clamp",
|
||||
[](float value) { return std::clamp(value, 0.0f, 1.0f); },
|
||||
mf::build::exec_presets::AllSpanOrSingle());
|
||||
return fn::Field<float>(fn::FieldOperation::Create(clamp_fn, {std::move(crease_field)}));
|
||||
}
|
||||
|
||||
static Mesh *mesh_subsurf_calc(const Mesh *mesh,
|
||||
const int level,
|
||||
const Field<float> &vert_crease_field,
|
||||
const Field<float> &edge_crease_field,
|
||||
const int boundary_smooth,
|
||||
const int uv_smooth)
|
||||
{
|
||||
const bke::MeshFieldContext point_context{*mesh, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator point_evaluator(point_context, mesh->totvert);
|
||||
point_evaluator.add(clamp_crease(vert_crease_field));
|
||||
point_evaluator.evaluate();
|
||||
|
||||
const bke::MeshFieldContext edge_context{*mesh, ATTR_DOMAIN_EDGE};
|
||||
FieldEvaluator edge_evaluator(edge_context, mesh->totedge);
|
||||
edge_evaluator.add(clamp_crease(edge_crease_field));
|
||||
edge_evaluator.evaluate();
|
||||
|
||||
const VArray<float> vert_creases = point_evaluator.get_evaluated<float>(0);
|
||||
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
|
||||
const bool use_creases = !varray_is_single_zero(vert_creases) ||
|
||||
!varray_is_single_zero(edge_creases);
|
||||
|
||||
Mesh *mesh_copy = nullptr;
|
||||
if (use_creases) {
|
||||
/* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
|
||||
* they are provided as separate inputs, not as custom data layers. When needed, retrieve the
|
||||
* mesh with write access and store the new crease values there. */
|
||||
mesh_copy = BKE_mesh_copy_for_eval(mesh);
|
||||
write_vert_creases(*mesh_copy, vert_creases);
|
||||
write_edge_creases(*mesh_copy, edge_creases);
|
||||
mesh = mesh_copy;
|
||||
}
|
||||
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
|
||||
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
mesh_settings.resolution = (1 << level) + 1;
|
||||
mesh_settings.use_optimal_display = false;
|
||||
|
||||
SubdivSettings subdiv_settings;
|
||||
subdiv_settings.is_simple = false;
|
||||
subdiv_settings.is_adaptive = false;
|
||||
subdiv_settings.use_creases = use_creases;
|
||||
subdiv_settings.level = level;
|
||||
subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
|
||||
boundary_smooth);
|
||||
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
|
||||
uv_smooth);
|
||||
|
||||
Subdiv *subdiv = BKE_subdiv_new_from_mesh(&subdiv_settings, mesh);
|
||||
if (!subdiv) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
||||
if (use_creases) {
|
||||
/* Remove the layer in case it was created by the node from the field input. The fact
|
||||
* that this node uses #CD_CREASE to input creases to the subdivision code is meant to be
|
||||
* an implementation detail ideally. */
|
||||
CustomData_free_layers(&result->edata, CD_CREASE, result->totedge);
|
||||
}
|
||||
|
||||
if (mesh_copy) {
|
||||
BKE_id_free(nullptr, mesh_copy);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void write_edge_creases(Mesh &mesh, const VArray<float> &crease_varray)
|
||||
{
|
||||
bke::SpanAttributeWriter<float> attribute =
|
||||
mesh.attributes_for_write().lookup_or_add_for_write_only_span<float>("crease",
|
||||
ATTR_DOMAIN_EDGE);
|
||||
materialize_and_clamp_creases(crease_varray, attribute.span);
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
static bool varray_is_nonzero(const VArray<float> &varray)
|
||||
{
|
||||
return !(varray.is_single() && varray.get_internal_single() == 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
|
||||
#ifndef WITH_OPENSUBDIV
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
|
||||
#else
|
||||
Field<float> edge_crease_field = params.extract_input<Field<float>>("Edge Crease");
|
||||
Field<float> vertex_crease_field = params.extract_input<Field<float>>("Vertex Crease");
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
const Field<float> vert_crease = params.extract_input<Field<float>>("Vertex Crease");
|
||||
const Field<float> edge_crease = params.extract_input<Field<float>>("Edge Crease");
|
||||
|
||||
const NodeGeometrySubdivisionSurface &storage = node_storage(params.node());
|
||||
const int uv_smooth = storage.uv_smooth;
|
||||
const int boundary_smooth = storage.boundary_smooth;
|
||||
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
|
||||
|
||||
/* Only process subdivision if level is greater than 0. */
|
||||
if (subdiv_level == 0) {
|
||||
const int level = std::clamp(params.extract_input<int>("Level"), 0, 11);
|
||||
if (level == 0) {
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
return;
|
||||
}
|
||||
|
||||
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
|
||||
const Mesh *mesh = geometry_set.get_mesh_for_read();
|
||||
if (!mesh) {
|
||||
return;
|
||||
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
|
||||
geometry_set.replace_mesh(
|
||||
mesh_subsurf_calc(mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth));
|
||||
}
|
||||
if (mesh->totvert == 0 || mesh->totedge == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bke::MeshFieldContext point_context{*mesh, ATTR_DOMAIN_POINT};
|
||||
FieldEvaluator point_evaluator(point_context, mesh->totvert);
|
||||
point_evaluator.add(vertex_crease_field);
|
||||
point_evaluator.evaluate();
|
||||
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
|
||||
|
||||
bke::MeshFieldContext edge_context{*mesh, ATTR_DOMAIN_EDGE};
|
||||
FieldEvaluator edge_evaluator(edge_context, mesh->totedge);
|
||||
edge_evaluator.add(edge_crease_field);
|
||||
edge_evaluator.evaluate();
|
||||
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
|
||||
|
||||
const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases);
|
||||
|
||||
if (use_creases) {
|
||||
/* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
|
||||
* they are provided as separate inputs, not as custom data layers. When needed, retrieve the
|
||||
* mesh with write access and store the new crease values there. */
|
||||
Mesh &mesh_for_write = *geometry_set.get_mesh_for_write();
|
||||
write_vertex_creases(mesh_for_write, vertex_creases);
|
||||
write_edge_creases(mesh_for_write, edge_creases);
|
||||
mesh = &mesh_for_write;
|
||||
}
|
||||
|
||||
/* Initialize mesh settings. */
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
mesh_settings.resolution = (1 << subdiv_level) + 1;
|
||||
mesh_settings.use_optimal_display = false;
|
||||
|
||||
/* Initialize subdivision settings. */
|
||||
SubdivSettings subdiv_settings;
|
||||
subdiv_settings.is_simple = false;
|
||||
subdiv_settings.is_adaptive = false;
|
||||
subdiv_settings.use_creases = use_creases;
|
||||
subdiv_settings.level = subdiv_level;
|
||||
|
||||
subdiv_settings.vtx_boundary_interpolation =
|
||||
BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth);
|
||||
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
|
||||
uv_smooth);
|
||||
|
||||
/* Apply subdivision to mesh. */
|
||||
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh);
|
||||
|
||||
/* In case of bad topology, skip to input mesh. */
|
||||
if (subdiv == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
BKE_subdiv_free(subdiv);
|
||||
if (use_creases) {
|
||||
/* Remove the layer in case it was created by the node from the field input. The fact
|
||||
* that this node uses #CD_CREASE to input creases to the subdivision code is meant to be
|
||||
* an implementation detail ideally. */
|
||||
CustomData_free_layers(&result->edata, CD_CREASE, result->totedge);
|
||||
}
|
||||
|
||||
geometry_set.replace_mesh(result);
|
||||
});
|
||||
#else
|
||||
params.error_message_add(NodeWarningType::Error,
|
||||
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
|
||||
|
||||
#endif
|
||||
params.set_output("Mesh", std::move(geometry_set));
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
@ -341,10 +502,10 @@ static void execute_multi_function_on_value_or_field(
|
|||
/* Construct the new field node. */
|
||||
std::shared_ptr<fn::FieldOperation> operation;
|
||||
if (owned_fn) {
|
||||
operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields));
|
||||
operation = fn::FieldOperation::Create(owned_fn, std::move(input_fields));
|
||||
}
|
||||
else {
|
||||
operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields));
|
||||
operation = fn::FieldOperation::Create(fn, std::move(input_fields));
|
||||
}
|
||||
|
||||
/* Store the new fields in the output. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2060,7 +2060,7 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
|
|||
BLI_strncpy(filepath, argv[0], sizeof(filepath));
|
||||
BLI_path_slash_native(filepath);
|
||||
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
|
||||
BLI_path_normalize(NULL, filepath);
|
||||
BLI_path_normalize(filepath);
|
||||
|
||||
/* load the file */
|
||||
BKE_reports_init(&reports, RPT_PRINT);
|
||||
|
|
Loading…
Reference in New Issue