Brush Assets: Store assets outside Main #117730

Manually merged
Brecht Van Lommel merged 18 commits from brecht/blender:brush-separate-main into brush-assets-project 2024-02-27 14:53:03 +01:00
23 changed files with 563 additions and 564 deletions
Showing only changes of commit befe409e30 - Show all commits

View File

@ -111,6 +111,19 @@ static const char *get_egl_error_message_string(EGLint error)
}
}
static void egl_print_error(const char *message, const EGLint error)
{
const char *code = get_egl_error_enum_string(error);
const char *msg = get_egl_error_message_string(error);
fprintf(stderr,
"%sEGL Error (0x%04X): %s: %s\n",
message,
uint(error),
code ? code : "<Unknown>",
msg ? msg : "<Unknown>");
}
static bool egl_chk(bool result,
const char *file = nullptr,
int line = 0,
@ -132,11 +145,7 @@ static bool egl_chk(bool result,
code ? code : "<Unknown>",
msg ? msg : "<Unknown>");
#else
fprintf(stderr,
"EGL Error (0x%04X): %s: %s\n",
uint(error),
code ? code : "<Unknown>",
msg ? msg : "<Unknown>");
egl_print_error("", error);
(void)(file);
(void)(line);
(void)(text);
@ -343,33 +352,43 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
goto error;
}
if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor)) ||
(egl_major == 0 && egl_minor == 0))
{
/* We failed to create a regular render window, retry and see if we can create a headless
* render context. */
::eglTerminate(m_display);
const EGLBoolean init_display_result = ::eglInitialize(m_display, &egl_major, &egl_minor);
const EGLint init_display_error = (init_display_result) ? 0 : eglGetError();
const char *egl_extension_st = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
assert(egl_extension_st != nullptr);
assert(egl_extension_st == nullptr ||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") != nullptr);
if (egl_extension_st == nullptr ||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") == nullptr)
{
goto error;
if (!init_display_result || (egl_major == 0 && egl_minor == 0)) {
/* We failed to create a regular render window, retry and see if we can create a headless
* render context. */
::eglTerminate(m_display);
const char *egl_extension_st = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
assert(egl_extension_st != nullptr);
assert(egl_extension_st == nullptr ||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") != nullptr);
if (egl_extension_st == nullptr ||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") == nullptr)
{
egl_print_error("Failed to create display GPU context: ", init_display_error);
fprintf(
stderr,
"Failed to create headless GPU context: No EGL_MESA_platform_surfaceless extension");
goto error;
}
m_display = eglGetPlatformDisplayEXT(
EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
const EGLBoolean headless_result = ::eglInitialize(m_display, &egl_major, &egl_minor);
const EGLint init_headless_error = (headless_result) ? 0 : eglGetError();
if (!headless_result) {
egl_print_error("Failed to create display GPU context: ", init_display_error);
egl_print_error("Failed to create headless GPU context: ", init_headless_error);
goto error;
}
}
m_display = eglGetPlatformDisplayEXT(
EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
if (!EGL_CHK(::eglInitialize(m_display, &egl_major, &egl_minor))) {
goto error;
}
/* Because the first eglInitialize will print an error to the terminal, print a "success"
* message here to let the user know that we successfully recovered from the error. */
fprintf(stderr, "\nManaged to successfully fallback to surfaceless EGL rendering!\n\n");
}
#ifdef WITH_GHOST_DEBUG
fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
#endif

View File

@ -1780,6 +1780,8 @@ static void ghost_wayland_log_handler(const char *msg, va_list arg)
__attribute__((format(printf, 1, 0)));
#endif
static bool ghost_wayland_log_handler_is_background = false;
/**
* Callback for WAYLAND to run when there is an error.
*
@ -1788,6 +1790,15 @@ static void ghost_wayland_log_handler(const char *msg, va_list arg)
*/
static void ghost_wayland_log_handler(const char *msg, va_list arg)
{
/* This is fine in background mode, we will try to fall back to headless GPU context.
* Happens when render farm process runs without user login session. */
if (ghost_wayland_log_handler_is_background &&
(strstr(msg, "error: XDG_RUNTIME_DIR not set in the environment") ||
strstr(msg, "error: XDG_RUNTIME_DIR is invalid or not set in the environment")))
{
return;
}
fprintf(stderr, "GHOST/Wayland: ");
vfprintf(stderr, msg, arg); /* Includes newline. */
@ -6902,6 +6913,7 @@ GHOST_SystemWayland::GHOST_SystemWayland(bool background)
#endif
display_(new GWL_Display)
{
ghost_wayland_log_handler_is_background = background;
wl_log_set_handler_client(ghost_wayland_log_handler);
display_->system = this;

View File

@ -339,7 +339,7 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &ot
public: \
void *operator new(size_t num_bytes) \
{ \
return MEM_mallocN(num_bytes, _id); \
return MEM_mallocN_aligned(num_bytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__, _id); \
} \
void *operator new(size_t num_bytes, std::align_val_t alignment) \
{ \
@ -353,7 +353,7 @@ template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &ot
} \
void *operator new[](size_t num_bytes) \
{ \
return MEM_mallocN(num_bytes, _id "[]"); \
return MEM_mallocN_aligned(num_bytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__, _id "[]"); \
} \
void *operator new[](size_t num_bytes, std::align_val_t alignment) \
{ \

View File

@ -23,8 +23,6 @@ set(SRC
intern/blf_font_default.cc
intern/blf_glyph.cc
intern/blf_thumbs.cc
intern/blf_util.cc
BLF_api.hh
intern/blf_internal.hh
intern/blf_internal_types.hh

View File

@ -91,7 +91,7 @@ static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font)
static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
{
std::unique_ptr<GlyphCacheBLF> gc = std::make_unique<GlyphCacheBLF>(GlyphCacheBLF{});
std::unique_ptr<GlyphCacheBLF> gc = std::make_unique<GlyphCacheBLF>();
gc->size = font->size;
gc->bold = ((font->flags & BLF_BOLD) != 0);
@ -101,8 +101,6 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
gc->char_width = font->char_width;
gc->char_spacing = font->char_spacing;
memset(gc->bucket, 0, sizeof(gc->bucket));
blf_ensure_size(font);
/* Determine ideal fixed-width size for monospaced output. */
@ -146,11 +144,7 @@ void blf_glyph_cache_release(FontBLF *font)
GlyphCacheBLF::~GlyphCacheBLF()
{
for (uint i = 0; i < ARRAY_SIZE(this->bucket); i++) {
while (GlyphBLF *g = static_cast<GlyphBLF *>(BLI_pophead(&this->bucket[i]))) {
blf_glyph_free(g);
}
}
this->glyphs.clear_and_shrink();
if (this->texture) {
GPU_texture_free(this->texture);
}
@ -174,12 +168,10 @@ static GlyphBLF *blf_glyph_cache_find_glyph(const GlyphCacheBLF *gc,
uint charcode,
uint8_t subpixel)
{
GlyphBLF *g = static_cast<GlyphBLF *>(gc->bucket[blf_hash(charcode << 6 | subpixel)].first);
while (g) {
if (g->c == charcode && g->subpixel == subpixel) {
return g;
}
g = g->next;
const std::unique_ptr<GlyphBLF> *ptr = gc->glyphs.lookup_ptr_as(
GlyphCacheKey{charcode, subpixel});
if (ptr != nullptr) {
return ptr->get();
}
return nullptr;
}
@ -231,7 +223,7 @@ static GlyphBLF *blf_glyph_cache_add_glyph(FontBLF *font,
FT_UInt glyph_index,
uint8_t subpixel)
{
GlyphBLF *g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get");
std::unique_ptr<GlyphBLF> g = std::make_unique<GlyphBLF>();
g->c = charcode;
g->idx = glyph_index;
g->advance_x = (ft_pix)glyph->advance.x;
@ -344,9 +336,9 @@ static GlyphBLF *blf_glyph_cache_add_glyph(FontBLF *font,
}
}
BLI_addhead(&(gc->bucket[blf_hash(g->c << 6 | subpixel)]), g);
return g;
GlyphCacheKey key = {charcode, subpixel};
gc->glyphs.add(key, std::move(g));
return gc->glyphs.lookup(key).get();
}
/** \} */
@ -1326,12 +1318,11 @@ GlyphBLF *blf_glyph_ensure_subpixel(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *
}
#endif
void blf_glyph_free(GlyphBLF *g)
GlyphBLF::~GlyphBLF()
{
if (g->bitmap) {
MEM_freeN(g->bitmap);
if (this->bitmap) {
MEM_freeN(this->bitmap);
}
MEM_freeN(g);
}
/** \} */

View File

@ -54,8 +54,6 @@ extern struct FontBLF *global_font[BLF_MAX_FONT];
void blf_batch_draw_begin(struct FontBLF *font);
void blf_batch_draw(void);
unsigned int blf_next_p2(unsigned int x);
unsigned int blf_hash(unsigned int val);
/**
* Some font have additional file with metrics information,
* in general, the extension of the file is: `.afm` or `.pfm`
@ -200,7 +198,6 @@ float blf_character_to_curves(FontBLF *font,
struct ListBase *nurbsbase,
const float scale);
void blf_glyph_free(struct GlyphBLF *g);
void blf_glyph_draw(
struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, int x, int y);

View File

@ -10,6 +10,7 @@
#include <mutex>
#include "BLI_map.hh"
#include "BLI_vector.hh"
#include "GPU_texture.h"
@ -116,6 +117,19 @@ struct KerningCacheBLF {
int ascii_table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE];
};
struct GlyphCacheKey {
uint charcode;
uint8_t subpixel;
friend bool operator==(const GlyphCacheKey &a, const GlyphCacheKey &b)
{
return a.charcode == b.charcode && a.subpixel == b.subpixel;
}
uint64_t hash() const
{
return blender::get_default_hash(charcode, subpixel);
}
};
struct GlyphCacheBLF {
/** Font size. */
float size;
@ -132,7 +146,7 @@ struct GlyphCacheBLF {
int fixed_width;
/** The glyphs. */
ListBase bucket[257];
blender::Map<GlyphCacheKey, std::unique_ptr<GlyphBLF>> glyphs;
/** Texture array, to draw the glyphs. */
GPUTexture *texture;
@ -145,9 +159,6 @@ struct GlyphCacheBLF {
};
struct GlyphBLF {
GlyphBLF *next;
GlyphBLF *prev;
/** The character, as UTF-32. */
unsigned int c;
@ -192,6 +203,8 @@ struct GlyphBLF {
int pos[2];
GlyphCacheBLF *glyph_cache;
~GlyphBLF();
};
struct FontBufInfoBLF {

View File

@ -1,43 +0,0 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blf
*
* Internal utility API for BLF.
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "BLI_utildefines.h"
#include "blf_internal.hh"
uint blf_next_p2(uint x)
{
x -= 1;
x |= (x >> 16);
x |= (x >> 8);
x |= (x >> 4);
x |= (x >> 2);
x |= (x >> 1);
x += 1;
return x;
}
uint blf_hash(uint val)
{
uint key;
key = val;
key += ~(key << 16);
key ^= (key >> 5);
key += (key << 3);
key ^= (key >> 13);
key += ~(key << 9);
key ^= (key >> 17);
return key % 257;
}

View File

@ -1894,7 +1894,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
if (lib->id.tag & LIB_TAG_DOIT) {
id_us_clear_real(&lib->id);
if (lib->id.us == 0) {
BKE_id_free(bmain, (ID *)lib);
BKE_id_delete(bmain, lib);
}
}
}

View File

@ -624,6 +624,7 @@ struct UnusedIDsData {
do_local_ids(parameters.do_local_ids),
do_linked_ids(parameters.do_linked_ids),
do_recursive(parameters.do_recursive),
filter_fn(parameters.filter_fn),
num_total(&parameters.num_total),
num_local(&parameters.num_local),
num_linked(&parameters.num_linked)

View File

@ -71,6 +71,13 @@ void Instance::init(const int2 &output_res,
if (output_res != film.display_extent_get()) {
sampling.reset();
}
if (output_rect) {
int2 offset = int2(output_rect->xmin, output_rect->ymin);
int2 extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
if (offset != film.get_data().offset || extent != film.get_data().extent) {
sampling.reset();
}
}
if (assign_if_different(overlays_enabled_, v3d && !(v3d->flag2 & V3D_HIDE_OVERLAYS))) {
sampling.reset();
}

View File

@ -74,7 +74,7 @@ vec3 lightprobe_spherical_sample_normalized_with_parallax(
float pdf_to_lod(float pdf)
{
return 1.0; /* TODO */
return 0.0; /* TODO */
}
vec3 lightprobe_eval_direction(LightProbeSample samp, vec3 P, vec3 L, float pdf)

View File

@ -35,6 +35,7 @@
struct SnapGizmo3D {
wmGizmo gizmo;
V3DSnapCursorState *snap_state;
V3DSnapCursorState snap_state_stored;
};
/* -------------------------------------------------------------------- */
@ -188,6 +189,7 @@ static void gizmo_snap_rna_snap_srouce_type_set_fn(PointerRNA * /*ptr*/,
static void snap_cursor_free(SnapGizmo3D *snap_gizmo)
{
if (snap_gizmo->snap_state) {
snap_gizmo->snap_state_stored = *snap_gizmo->snap_state;
ED_view3d_cursor_snap_state_free(snap_gizmo->snap_state);
snap_gizmo->snap_state = nullptr;
}
@ -216,18 +218,6 @@ static bool snap_cursor_poll(ARegion *region, void *data)
return true;
}
static void snap_cursor_init(SnapGizmo3D *snap_gizmo)
{
snap_gizmo->snap_state = ED_view3d_cursor_snap_state_create();
snap_gizmo->snap_state->draw_point = true;
snap_gizmo->snap_state->draw_plane = false;
rgba_float_to_uchar(snap_gizmo->snap_state->target_color, snap_gizmo->gizmo.color);
snap_gizmo->snap_state->poll = snap_cursor_poll;
snap_gizmo->snap_state->poll_data = snap_gizmo;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -237,14 +227,23 @@ static void snap_cursor_init(SnapGizmo3D *snap_gizmo)
static void snap_gizmo_setup(wmGizmo *gz)
{
gz->flag |= WM_GIZMO_NO_TOOLTIP;
snap_cursor_init((SnapGizmo3D *)gz);
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
snap_gizmo->snap_state = ED_view3d_cursor_snap_state_create();
snap_gizmo->snap_state->draw_point = true;
snap_gizmo->snap_state->draw_plane = false;
snap_gizmo->snap_state->poll = snap_cursor_poll;
snap_gizmo->snap_state->poll_data = snap_gizmo;
snap_gizmo->snap_state_stored = *snap_gizmo->snap_state;
}
static void snap_gizmo_draw(const bContext * /*C*/, wmGizmo *gz)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
if (snap_gizmo->snap_state == nullptr) {
snap_cursor_init(snap_gizmo);
snap_gizmo->snap_state = ED_view3d_cursor_snap_state_create();
*snap_gizmo->snap_state = snap_gizmo->snap_state_stored;
}
/* All drawing is handled at the paint cursor.

View File

@ -116,7 +116,6 @@ struct KnifeVert {
int ob_index;
float co[3], cageco[3];
bool is_face, in_space;
bool is_cut; /* Along a cut created by user input (will draw too). */
bool is_invalid;
bool is_splitting; /* Created when an edge was split. */
@ -165,7 +164,7 @@ struct KnifePosData {
* -1 represents the absence of an object. */
int ob_index;
float mval[2]; /* Mouse screen position (may be non-integral if snapped to something). */
float2 mval; /* Mouse screen position (may be non-integral if snapped to something). */
bool is_space() const
{
@ -1837,20 +1836,15 @@ static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
/** \name Cut/Hit Utils
* \{ */
static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval);
/* User has just clicked for first time or first time after a restart (E key).
* Copy the current position data into prev. */
static void knife_start_cut(KnifeTool_OpData *kcd)
static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
{
knife_snap_curr(kcd, mval);
kcd->prev = kcd->curr;
kcd->mdata.is_stored = false;
if (kcd->prev.vert == nullptr && kcd->prev.edge == nullptr) {
float ofs_local[3];
negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, ofs_local, kcd->curr.mval, kcd->prev.cage);
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
}
}
static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh)
@ -2064,7 +2058,6 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd,
kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit);
kfe->v1->ob_index = lh1->ob_index;
kfe->v1->is_cut = true;
kfe->v1->is_face = true;
knife_append_list(kcd, &kfe->v1->faces, lh1->f);
lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
}
@ -2081,7 +2074,6 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd,
kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit);
kfe->v2->ob_index = lh2->ob_index;
kfe->v2->is_cut = true;
kfe->v2->is_face = true;
knife_append_list(kcd, &kfe->v2->faces, lh2->f);
lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */
}
@ -2830,7 +2822,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
KnifeLineHit *linehits = nullptr;
BLI_array_declare(linehits);
KnifeLineHit hit;
float s[2], se1[2], se2[2], sint[2];
float s[2], se1[2], se2[2];
float d1, d2;
float vert_tol, vert_tol_sq;
float line_tol, line_tol_sq;
@ -3016,55 +3008,66 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
knife_project_v2(kcd, kfe->v1->cageco, se1);
knife_project_v2(kcd, kfe->v2->cageco, se2);
int isect_kind = 1;
float3 p_cage;
float2 p_cage_ss;
bool kfe_is_in_cut = false;
if (kfe == kcd->prev.edge) {
/* This KnifeEdge was captured by the snap system. */
copy_v2_v2(sint, kcd->prev.mval);
p_cage = kcd->prev.cage;
p_cage_ss = kcd->prev.mval;
kfe_is_in_cut = true;
}
else if (kfe == kcd->curr.edge) {
/* This KnifeEdge was captured by the snap system. */
copy_v2_v2(sint, kcd->curr.mval);
p_cage = kcd->curr.cage;
p_cage_ss = kcd->curr.mval;
kfe_is_in_cut = true;
}
else {
isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, sint);
int isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, p_cage_ss);
if (isect_kind == -1) {
/* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */
closest_to_line_segment_v2(sint, s1, se1, se2);
if (len_squared_v2v2(sint, s1) <= line_tol_sq) {
closest_to_line_segment_v2(p_cage_ss, s1, se1, se2);
if (len_squared_v2v2(p_cage_ss, s1) <= line_tol_sq) {
isect_kind = 1;
}
else {
closest_to_line_segment_v2(sint, s2, se1, se2);
if (len_squared_v2v2(sint, s2) <= line_tol_sq) {
closest_to_line_segment_v2(p_cage_ss, s2, se1, se2);
if (len_squared_v2v2(p_cage_ss, s2) <= line_tol_sq) {
isect_kind = 1;
}
}
}
}
if (isect_kind == 1) {
d1 = len_v2v2(sint, se1);
d2 = len_v2v2(se2, se1);
if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
float3 r1, r2;
float p_cage[3], p_cage_tmp[3];
/* Can't just interpolate between ends of kfe because
* that doesn't work with perspective transformation.
* Need to find 3d intersection of ray through sint. */
knife_input_ray_segment(kcd, sint, r1, r2);
if (isect_kind == 1) {
d1 = len_v2v2(p_cage_ss, se1);
d2 = len_v2v2(se2, se1);
if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
float3 r1, r2;
float3 p_cage_dummy;
/* Can't just interpolate between ends of kfe because
* that doesn't work with perspective transformation.
* Need to find 3d intersection of ray through sint. */
knife_input_ray_segment(kcd, p_cage_ss, r1, r2);
isect_kind = isect_line_line_v3(
kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp);
if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, bm_elem_from_knife_edge(kfe))) {
if (kcd->snap_midpoints) {
/* Choose intermediate point snap too. */
mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(sint, se1, se2);
isect_kind = isect_line_line_v3(
kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_dummy);
if (isect_kind >= 1 &&
point_is_visible(kcd, p_cage, p_cage_ss, bm_elem_from_knife_edge(kfe)))
{
if (kcd->snap_midpoints) {
/* Choose intermediate point snap too. */
mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(p_cage_ss, se1, se2);
}
kfe_is_in_cut = true;
}
knife_linehit_set(kcd, s1, s2, sint, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
BLI_array_append(linehits, hit);
}
}
}
if (kfe_is_in_cut) {
knife_linehit_set(kcd, s1, s2, p_cage_ss, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
BLI_array_append(linehits, hit);
}
}
/* Now face hits; don't add if a vertex or edge in face should have hit. */
@ -3106,6 +3109,7 @@ static void knife_pos_data_clear(KnifePosData *kpd)
kpd->vert = nullptr;
kpd->edge = nullptr;
kpd->bmface = nullptr;
kpd->ob_index = -1;
zero_v2(kpd->mval);
}
@ -3115,21 +3119,23 @@ static void knife_pos_data_clear(KnifePosData *kpd)
/** \name Snapping (#knife_snap_update_from_mval)
* \{ */
static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, int *r_ob_index, float3 &r_cageco)
static bool knife_find_closest_face(KnifeTool_OpData *kcd, const float2 &mval, KnifePosData *r_kpd)
{
float3 cage;
int ob_index;
BMFace *f;
float dist = KMAXDIST;
float3 origin;
float3 ray_normal;
ED_view3d_win_to_ray_clipped(
kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, kcd->curr.mval, origin, ray_normal, false);
kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, origin, ray_normal, false);
f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, r_cageco, r_ob_index);
f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, cage, &ob_index);
if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) {
f = nullptr;
*r_ob_index = -1;
ob_index = -1;
}
if (f == nullptr) {
@ -3139,24 +3145,32 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, int *r_ob_index, f
* Apply the mouse coordinates to a copy of the view-context
* since we don't want to rely on this being set elsewhere. */
ViewContext vc = kcd->vc;
vc.mval[0] = int(kcd->curr.mval[0]);
vc.mval[1] = int(kcd->curr.mval[1]);
vc.mval[0] = int(mval[0]);
vc.mval[1] = int(mval[1]);
f = EDBM_face_find_nearest(&vc, &dist);
/* Cheat for now; just put in the origin instead
* of a true coordinate on the face.
* This just puts a point 1.0f in front of the view. */
r_cageco = origin + ray_normal;
if (f) {
*r_ob_index = 0;
BLI_assert(*r_ob_index == kcd->objects.first_index_of_try(vc.obact));
/* Cheat for now; just put in the origin instead
* of a true coordinate on the face.
* This just puts a point 1.0f in front of the view. */
cage = origin + ray_normal;
ob_index = 0;
BLI_assert(ob_index == kcd->objects.first_index_of_try(vc.obact));
}
}
}
return f;
if (f) {
r_kpd->cage = cage;
r_kpd->bmface = f;
r_kpd->ob_index = ob_index;
r_kpd->mval = mval;
return true;
}
return false;
}
/**
@ -3238,19 +3252,19 @@ static bool knife_snap_edge_constrained(KnifeTool_OpData *kcd,
const float kfv1_sco[2],
const float kfv2_sco[2],
float *r_dist_sq,
float *r_lambda)
float *r_lambda,
float2 &r_sco)
{
/* If snapping, check we're in bounds. */
float sco_snap[2];
isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, r_sco);
float lambda = line_point_factor_v2(r_sco, kfv1_sco, kfv2_sco);
/* Be strict when constrained within edge. */
if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
return false;
}
float dis_sq = len_squared_v2v2(sco, sco_snap);
float dis_sq = len_squared_v2v2(sco, r_sco);
if (dis_sq < *r_dist_sq) {
*r_dist_sq = dis_sq;
*r_lambda = lambda;
@ -3283,12 +3297,9 @@ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
}
/* p is closest point on edge to the mouse cursor. */
static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
int ob_index,
BMFace *f,
float cagep[3])
static bool knife_find_closest_edge_of_face(
KnifeTool_OpData *kcd, int ob_index, BMFace *f, const float2 &cage_ss, KnifePosData *r_kpd)
{
float sco[2];
float maxdist;
if (kcd->is_interactive) {
@ -3303,15 +3314,11 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
}
const float maxdist_sq = maxdist * maxdist;
KnifeEdge *cure = nullptr;
float cur_cagep[3];
ListBase *list;
float dis_sq, curdis_sq = maxdist_sq;
knife_project_v2(kcd, cagep, sco);
float cur_dist_sq = maxdist_sq;
bool has_hit = false;
/* Look through all edges associated with this face. */
list = knife_get_face_kedges(kcd, ob_index, f);
ListBase *list = knife_get_face_kedges(kcd, ob_index, f);
LISTBASE_FOREACH (LinkData *, ref, list) {
KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
@ -3327,18 +3334,23 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
/* Check if we're close enough and calculate 'lambda'. */
/* In constrained mode calculate lambda differently, unless constrained along kcd->prev.edge */
float2 closest_ss;
float dis_sq;
if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) &&
(kcd->mode == MODE_DRAGGING))
{
dis_sq = curdis_sq;
if (!knife_snap_edge_constrained(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) {
dis_sq = cur_dist_sq;
if (!knife_snap_edge_constrained(
kcd, cage_ss, kfv1_sco, kfv2_sco, &dis_sq, &lambda, closest_ss))
{
continue;
}
}
else {
dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
if (dis_sq < curdis_sq) {
lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
closest_to_line_segment_v2(closest_ss, cage_ss, kfv1_sco, kfv2_sco);
dis_sq = len_squared_v2v2(closest_ss, cage_ss);
if (dis_sq < cur_dist_sq) {
lambda = line_point_factor_v2(cage_ss, kfv1_sco, kfv2_sco);
}
else {
continue;
@ -3355,41 +3367,30 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
}
}
cure = kfe;
curdis_sq = dis_sq;
copy_v3_v3(cur_cagep, test_cagep);
}
if (cure && !kcd->ignore_edge_snapping) {
KnifeVert *edgesnap = nullptr;
float3 p;
cur_dist_sq = dis_sq;
r_kpd->edge = kfe;
if (kcd->snap_midpoints) {
mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
mid_v3_v3v3(r_kpd->cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(r_kpd->mval, kfv1_sco, kfv2_sco);
}
else {
float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
copy_v3_v3(cagep, cur_cagep);
interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
copy_v3_v3(r_kpd->cage, test_cagep);
r_kpd->mval = closest_ss;
}
/* Update mouse coordinates to the snapped-to edge's screen coordinates
* this is important for angle snap, which uses the previous mouse position. */
edgesnap = new_knife_vert(kcd, p, cagep);
edgesnap->ob_index = ob_index;
knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
has_hit = true;
}
return cure;
return has_hit;
}
/* Find a vertex near the mouse cursor, if it exists. */
static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
KnifeEdge *kfe,
float cagep[3])
static bool knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
const KnifeEdge *kfe,
const float2 &cage_ss,
KnifePosData *r_kpd)
{
float sco[2];
float maxdist;
if (kcd->is_interactive) {
@ -3407,8 +3408,6 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
float cur_kfv_sco[2];
float dis_sq, curdis_sq = FLT_MAX;
knife_project_v2(kcd, cagep, sco);
for (int i = 0; i < 2; i++) {
KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
float kfv_sco[2];
@ -3425,7 +3424,7 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
}
}
dis_sq = len_squared_v2v2(kfv_sco, sco);
dis_sq = len_squared_v2v2(kfv_sco, cage_ss);
if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
!ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false))
@ -3437,15 +3436,17 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
}
}
if (curv && !kcd->ignore_vert_snapping) {
copy_v3_v3(cagep, curv->cageco);
if (curv) {
r_kpd->cage = curv->cageco;
/* Update mouse coordinates to the snapped-to vertex's screen coordinates
* this is important for angle snap, which uses the previous mouse position. */
copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
r_kpd->mval = cur_kfv_sco;
return true;
}
return curv;
return false;
}
/**
@ -3765,6 +3766,51 @@ static void knife_constrain_axis(KnifeTool_OpData *kcd)
copy_v2_v2(kcd->mval, kcd->curr.mval);
}
static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval)
{
knife_pos_data_clear(&kcd->curr);
if (knife_find_closest_face(kcd, mval, &kcd->curr)) {
if (!kcd->ignore_edge_snapping || !kcd->ignore_vert_snapping) {
KnifePosData kpos_tmp = kcd->curr;
if (knife_find_closest_edge_of_face(
kcd, kpos_tmp.ob_index, kpos_tmp.bmface, kpos_tmp.mval, &kpos_tmp))
{
if (!kcd->ignore_edge_snapping) {
kcd->curr = kpos_tmp;
}
if (!kcd->ignore_vert_snapping) {
knife_find_closest_vert_of_edge(kcd, kpos_tmp.edge, kpos_tmp.mval, &kcd->curr);
}
}
}
}
if (kcd->curr.vert || kcd->curr.edge || kcd->curr.bmface) {
return;
}
/* If no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
* Note that drawing lines in `free-space` isn't properly supported
* but there's no guarantee (0, 0, 0) has any geometry either - campbell */
float origin[3];
float origin_ofs[3];
copy_v2_v2(kcd->curr.mval, mval);
knife_input_ray_segment(kcd, kcd->curr.mval, origin, origin_ofs);
if (!isect_line_plane_v3(
kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
{
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
/* Should never fail! */
BLI_assert(0);
}
}
/**
* \return true when `kcd->curr.co` & `kcd->curr.cage` are set.
*
@ -3773,7 +3819,7 @@ static void knife_constrain_axis(KnifeTool_OpData *kcd)
* In this case the selection-buffer is used to select the face,
* then the closest `vert` or `edge` is set, and those will enable `is_co_set`.
*/
static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
copy_v2_v2(kcd->curr.mval, mval);
@ -3800,25 +3846,7 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[
}
}
{
kcd->curr.ob_index = 0; /* Start with a valid object. */
kcd->curr.bmface = knife_find_closest_face(kcd, &kcd->curr.ob_index, kcd->curr.cage);
if (kcd->curr.bmface) {
kcd->curr.edge = knife_find_closest_edge_of_face(
kcd, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage);
}
if (kcd->curr.edge) {
kcd->curr.vert = knife_find_closest_vert_of_edge(kcd, kcd->curr.edge, kcd->curr.cage);
if (kcd->ignore_edge_snapping) {
kcd->curr.edge = nullptr;
}
}
}
return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space());
knife_snap_curr(kcd, mval);
}
/**
@ -4139,25 +4167,7 @@ static void knifetool_exit(wmOperator *op)
/** Update active knife edge/vert pointers. */
static int knife_update_active(KnifeTool_OpData *kcd)
{
/* If no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
* Note that drawing lines in `free-space` isn't properly supported
* but there's no guarantee (0, 0, 0) has any geometry either - campbell */
if (!knife_snap_update_from_mval(kcd, kcd->mval)) {
float origin[3];
float origin_ofs[3];
knife_input_ray_segment(kcd, kcd->curr.mval, origin, origin_ofs);
if (!isect_line_plane_v3(
kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
{
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
/* Should never fail! */
BLI_assert(0);
}
}
knife_snap_update_from_mval(kcd, kcd->mval);
if (kcd->mode == MODE_DRAGGING) {
knife_find_line_hits(kcd);
@ -4502,7 +4512,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
knife_add_cut(kcd);
}
else if (kcd->mode != MODE_PANNING) {
knife_start_cut(kcd);
knife_start_cut(kcd, float2(event->mval));
kcd->mode = MODE_DRAGGING;
kcd->init = kcd->curr;
}
@ -4719,8 +4729,6 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
WM_event_add_modal_handler(C, op);
knifetool_update_mval_i(kcd, event->mval);
if (wait_for_input == false) {
/* Avoid copy-paste logic. */
wmEvent event_modal{};
@ -4728,6 +4736,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
event_modal.type = EVT_MODAL_MAP;
event_modal.val = KNF_MODAL_ADD_CUT;
copy_v2_v2_int(event_modal.mval, event->mval);
int ret = knifetool_modal(C, op, &event_modal);
BLI_assert(ret == OPERATOR_RUNNING_MODAL);
UNUSED_VARS_NDEBUG(ret);
@ -4880,16 +4890,14 @@ void EDBM_mesh_knife(
const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
int i;
for (i = 0; i < mval_tot; i++) {
knife_start_cut(kcd, mval_fl[0]);
kcd->mode = MODE_DRAGGING;
for (i = 1; i < mval_tot; i++) {
knifetool_update_mval(kcd, mval_fl[i]);
if (i == 0) {
knife_start_cut(kcd);
kcd->mode = MODE_DRAGGING;
}
else {
knife_add_cut(kcd);
}
knife_add_cut(kcd);
}
knife_finish_cut(kcd);
kcd->mode = MODE_IDLE;
p = p->next;

View File

@ -374,8 +374,7 @@ void item_rename_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
ARegion *region = CTX_wm_region(C);
do_item_rename(region, te, tselem, reports);
@ -521,8 +520,7 @@ void id_delete_tag_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
id_delete_tag(C, reports, te, tselem);
}
@ -766,8 +764,7 @@ void id_remap_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false);
PointerRNA op_props;
@ -1007,8 +1004,7 @@ void lib_relocate_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
/* XXX: This does not work with several items
* (it is only called once in the end, due to the 'deferred'
@ -1064,8 +1060,7 @@ void lib_reload_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_reload", false);

View File

@ -10,6 +10,8 @@
#include <memory>
#include "BLI_function_ref.hh"
#include "RNA_types.hh"
/* Needed for `tree_element_cast()`. */
@ -382,13 +384,12 @@ bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const floa
void outliner_item_mode_toggle(bContext *C, TreeViewContext *tvc, TreeElement *te, bool do_extend);
/* `outliner_edit.cc` */
using outliner_operation_fn = void (*)(bContext *C,
ReportList *,
Scene *scene,
TreeElement *,
TreeStoreElem *,
TreeStoreElem *,
void *);
using outliner_operation_fn = blender::FunctionRef<void(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem)>;
/**
* \param recurse_selected: Set to false for operations which are already
@ -400,7 +401,6 @@ void outliner_do_object_operation_ex(bContext *C,
SpaceOutliner *space_outliner,
ListBase *lb,
outliner_operation_fn operation_fn,
void *user_data,
bool recurse_selected);
void outliner_do_object_operation(bContext *C,
ReportList *reports,
@ -424,37 +424,32 @@ void item_rename_fn(bContext *C,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data);
TreeStoreElem *tselem);
void lib_relocate_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data);
TreeStoreElem *tselem);
void lib_reload_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data);
TreeStoreElem *tselem);
void id_delete_tag_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data);
TreeStoreElem *tselem);
void id_remap_fn(bContext *C,
ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data);
TreeStoreElem *tselem);
/**
* To retrieve coordinates with redrawing the entire tree.

View File

@ -222,8 +222,7 @@ static void unlink_action_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
if (!tsep || !TSE_IS_REAL_ID(tsep)) {
/* Valid case, no parent element of the action or it is not an ID (could be a #TSE_ID_BASE
@ -247,8 +246,7 @@ static void unlink_material_fn(bContext * /*C*/,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA);
@ -335,8 +333,7 @@ static void unlink_texture_fn(bContext * /*C*/,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
if (!tsep || !TSE_IS_REAL_ID(tsep)) {
/* Valid case, no parent element of the texture or it is not an ID (could be a #TSE_ID_BASE
@ -375,8 +372,7 @@ static void unlink_collection_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
Main *bmain = CTX_data_main(C);
Collection *collection = (Collection *)tselem->id;
@ -432,8 +428,7 @@ static void unlink_object_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
if (tsep && tsep->id) {
Main *bmain = CTX_data_main(C);
@ -497,8 +492,7 @@ static void unlink_world_fn(bContext * /*C*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
if (!tsep || !TSE_IS_REAL_ID(tsep)) {
/* Valid case, no parent element of the world or it is not an ID (could be a #TSE_ID_BASE
@ -523,8 +517,7 @@ static void outliner_do_libdata_operation(bContext *C,
ReportList *reports,
Scene *scene,
SpaceOutliner *space_outliner,
outliner_operation_fn operation_fn,
void *user_data)
outliner_operation_fn operation_fn)
{
tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
TreeStoreElem *tselem = TREESTORE(te);
@ -533,7 +526,7 @@ static void outliner_do_libdata_operation(bContext *C,
tselem->type == TSE_LAYER_COLLECTION)
{
TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : nullptr;
operation_fn(C, reports, scene, te, tsep, tselem, user_data);
operation_fn(C, reports, scene, te, tsep, tselem);
}
}
});
@ -576,8 +569,7 @@ static bool outliner_do_libdata_operation_selection_set_element(
TreeStoreElem *tselem,
const bool has_parent_selected,
outliner_operation_fn operation_fn,
eOutlinerLibOpSelectionSet selection_set,
void *user_data)
eOutlinerLibOpSelectionSet selection_set)
{
const bool do_selected = ELEM(selection_set,
OUTLINER_LIB_SELECTIONSET_SELECTED,
@ -592,7 +584,7 @@ static bool outliner_do_libdata_operation_selection_set_element(
tselem->type == TSE_LAYER_COLLECTION)
{
TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr;
operation_fn(C, reports, scene, element, tsep, tselem, user_data);
operation_fn(C, reports, scene, element, tsep, tselem);
}
}
return is_selected;
@ -605,8 +597,7 @@ static void outliner_do_libdata_operation_selection_set(bContext *C,
const ListBase &subtree,
const bool has_parent_selected,
outliner_operation_fn operation_fn,
eOutlinerLibOpSelectionSet selection_set,
void *user_data)
eOutlinerLibOpSelectionSet selection_set)
{
LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) {
/* Get needed data out in case element gets freed. */
@ -614,15 +605,7 @@ static void outliner_do_libdata_operation_selection_set(bContext *C,
const ListBase subtree = element->subtree;
const bool is_selected = outliner_do_libdata_operation_selection_set_element(
C,
reports,
scene,
element,
tselem,
has_parent_selected,
operation_fn,
selection_set,
user_data);
C, reports, scene, element, tselem, has_parent_selected, operation_fn, selection_set);
/* Don't access element from now on, it may be freed. Note that the open/collapsed state may
* also have been changed in the visitor callback. */
@ -633,8 +616,7 @@ static void outliner_do_libdata_operation_selection_set(bContext *C,
subtree,
is_selected || has_parent_selected,
operation_fn,
selection_set,
user_data);
selection_set);
}
}
@ -644,7 +626,6 @@ static void outliner_do_libdata_operation_selection_set(bContext *C,
SpaceOutliner *space_outliner,
outliner_operation_fn operation_fn,
eOutlinerLibOpSelectionSet selection_set,
void *user_data,
const bool do_active_element_first)
{
if (do_active_element_first) {
@ -654,39 +635,18 @@ static void outliner_do_libdata_operation_selection_set(bContext *C,
TreeStoreElem *tselem = TREESTORE(active_element);
const ListBase subtree = active_element->subtree;
const bool is_selected = outliner_do_libdata_operation_selection_set_element(C,
reports,
scene,
active_element,
tselem,
false,
operation_fn,
selection_set,
user_data);
const bool is_selected = outliner_do_libdata_operation_selection_set_element(
C, reports, scene, active_element, tselem, false, operation_fn, selection_set);
/* Don't access element from now on, it may be freed. Note that the open/collapsed state may
* also have been changed in the visitor callback. */
outliner_do_libdata_operation_selection_set(C,
reports,
scene,
space_outliner,
subtree,
is_selected,
operation_fn,
selection_set,
user_data);
outliner_do_libdata_operation_selection_set(
C, reports, scene, space_outliner, subtree, is_selected, operation_fn, selection_set);
}
}
outliner_do_libdata_operation_selection_set(C,
reports,
scene,
space_outliner,
space_outliner->tree,
false,
operation_fn,
selection_set,
user_data);
outliner_do_libdata_operation_selection_set(
C, reports, scene, space_outliner, space_outliner->tree, false, operation_fn, selection_set);
}
/** \} */
@ -923,8 +883,7 @@ static void object_select_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -948,8 +907,7 @@ static void object_select_hierarchy_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem * /*tsep*/,
TreeStoreElem * /*tselem*/,
void * /*user_data*/)
TreeStoreElem * /*tselem*/)
{
/* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
* it's especially confusing when multiple items are selected since some toggle on/off. */
@ -963,8 +921,7 @@ static void object_deselect_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -1011,8 +968,7 @@ static void id_local_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
Main *bmain = CTX_data_main(C);
@ -1103,17 +1059,15 @@ struct OutlinerLibOverrideData {
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
ReportList *reports,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
static void id_override_library_create_hierarchy_pre_process(bContext *C,
OutlinerLibOverrideData *data,
ReportList *reports,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
ID *id_root_reference = tselem->id;
@ -1436,17 +1390,12 @@ static void id_override_library_create_hierarchy_process(bContext *C,
FOREACH_MAIN_ID_END;
}
static void id_override_library_reset_fn(bContext *C,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void *user_data)
static void id_override_library_reset(bContext *C,
OutlinerLibOverrideData *data,
TreeStoreElem *tselem)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
@ -1522,16 +1471,10 @@ static void id_override_library_clear_single_process(bContext *C,
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_SYNC_TO_EVAL);
}
static void id_override_library_clear_single_fn(bContext * /*C*/,
ReportList *reports,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void *user_data)
static void id_override_library_clear_single(OutlinerLibOverrideData *data,
ReportList *reports,
TreeStoreElem *tselem)
{
OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id = tselem->id;
@ -1558,17 +1501,10 @@ static void id_override_library_clear_single_fn(bContext * /*C*/,
data->id_root_set(id);
}
static void id_override_library_resync_fn(bContext * /*C*/,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void *user_data)
static void id_override_library_resync(OutlinerLibOverrideData *data, TreeStoreElem *tselem)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
@ -1607,16 +1543,9 @@ static void id_override_library_resync_hierarchy_process(bContext *C,
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
static void id_override_library_delete_hierarchy_fn(bContext * /*C*/,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void *user_data)
static void id_override_library_delete_hierarchy(OutlinerLibOverrideData *data,
TreeStoreElem *tselem)
{
OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
@ -1649,8 +1578,7 @@ static void id_fake_user_set_fn(bContext * /*C*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
ID *id = tselem->id;
@ -1662,8 +1590,7 @@ static void id_fake_user_clear_fn(bContext * /*C*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
ID *id = tselem->id;
@ -1675,8 +1602,7 @@ static void id_select_linked_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
ID *id = tselem->id;
@ -1688,8 +1614,7 @@ static void singleuser_action_fn(bContext *C,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
/* This callback runs for all selected elements, some of which may not be actions which results
* in a crash. */
@ -1715,8 +1640,7 @@ static void singleuser_world_fn(bContext *C,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem *tsep,
TreeStoreElem *tselem,
void * /*user_data*/)
TreeStoreElem *tselem)
{
ID *id = tselem->id;
@ -1738,7 +1662,6 @@ void outliner_do_object_operation_ex(bContext *C,
SpaceOutliner *space_outliner,
ListBase *lb,
outliner_operation_fn operation_fn,
void *user_data,
bool recurse_selected)
{
LISTBASE_FOREACH (TreeElement *, te, lb) {
@ -1754,21 +1677,14 @@ void outliner_do_object_operation_ex(bContext *C,
/* Important to use 'scene_owner' not scene_act else deleting objects can crash.
* only use 'scene_act' when 'scene_owner' is nullptr, which can happen when the
* outliner isn't showing scenes: Visible Layer draw mode for eg. */
operation_fn(
C, reports, scene_owner ? scene_owner : scene_act, te, nullptr, tselem, user_data);
operation_fn(C, reports, scene_owner ? scene_owner : scene_act, te, nullptr, tselem);
select_handled = true;
}
}
if (TSELEM_OPEN(tselem, space_outliner)) {
if ((select_handled == false) || recurse_selected) {
outliner_do_object_operation_ex(C,
reports,
scene_act,
space_outliner,
&te->subtree,
operation_fn,
nullptr,
recurse_selected);
outliner_do_object_operation_ex(
C, reports, scene_act, space_outliner, &te->subtree, operation_fn, recurse_selected);
}
}
}
@ -1781,8 +1697,7 @@ void outliner_do_object_operation(bContext *C,
ListBase *lb,
outliner_operation_fn operation_fn)
{
outliner_do_object_operation_ex(
C, reports, scene_act, space_outliner, lb, operation_fn, nullptr, true);
outliner_do_object_operation_ex(C, reports, scene_act, space_outliner, lb, operation_fn, true);
}
/** \} */
@ -1936,9 +1851,16 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
[&](bContext *C,
ReportList *reports,
Scene * /*scene*/,
TreeElement *te,
TreeStoreElem *tsep,
TreeStoreElem *tselem) {
id_override_library_create_hierarchy_pre_process(
C, &override_data, reports, te, tsep, tselem);
},
selection_set,
&override_data,
true);
id_override_library_create_hierarchy_process(C, op->reports, override_data);
@ -1948,14 +1870,19 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_LIBOVERRIDE_OP_RESET: {
OutlinerLibOverrideData override_data{};
outliner_do_libdata_operation_selection_set(C,
op->reports,
scene,
space_outliner,
id_override_library_reset_fn,
selection_set,
&override_data,
false);
outliner_do_libdata_operation_selection_set(
C,
op->reports,
scene,
space_outliner,
[&](bContext *C,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem) { id_override_library_reset(C, &override_data, tselem); },
selection_set,
false);
ED_undo_push(C, "Reset Overridden Data");
break;
}
@ -1964,14 +1891,21 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
override_data.do_hierarchy = false;
override_data.do_fully_editable = false;
outliner_do_libdata_operation_selection_set(C,
op->reports,
scene,
space_outliner,
id_override_library_clear_single_fn,
selection_set,
&override_data,
false);
outliner_do_libdata_operation_selection_set(
C,
op->reports,
scene,
space_outliner,
[&](bContext * /*C*/,
ReportList *reports,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem) {
id_override_library_clear_single(&override_data, reports, tselem);
},
selection_set,
false);
id_override_library_clear_single_process(C, op->reports, override_data);
@ -1982,14 +1916,19 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
outliner_do_libdata_operation_selection_set(C,
op->reports,
scene,
space_outliner,
id_override_library_resync_fn,
OUTLINER_LIB_SELECTIONSET_SELECTED,
&override_data,
false);
outliner_do_libdata_operation_selection_set(
C,
op->reports,
scene,
space_outliner,
[&](bContext * /*C*/,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem) { id_override_library_resync(&override_data, tselem); },
OUTLINER_LIB_SELECTIONSET_SELECTED,
false);
id_override_library_resync_hierarchy_process(C, op->reports, override_data);
@ -2000,14 +1939,19 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_resync_hierarchy_enforce = true;
outliner_do_libdata_operation_selection_set(C,
op->reports,
scene,
space_outliner,
id_override_library_resync_fn,
OUTLINER_LIB_SELECTIONSET_SELECTED,
&override_data,
false);
outliner_do_libdata_operation_selection_set(
C,
op->reports,
scene,
space_outliner,
[&](bContext * /*C*/,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem) { id_override_library_resync(&override_data, tselem); },
OUTLINER_LIB_SELECTIONSET_SELECTED,
false);
id_override_library_resync_hierarchy_process(C, op->reports, override_data);
@ -2017,14 +1961,21 @@ static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY: {
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
outliner_do_libdata_operation_selection_set(C,
op->reports,
scene,
space_outliner,
id_override_library_delete_hierarchy_fn,
OUTLINER_LIB_SELECTIONSET_SELECTED,
&override_data,
false);
outliner_do_libdata_operation_selection_set(
C,
op->reports,
scene,
space_outliner,
[&](bContext * /*C*/,
ReportList * /*reports*/,
Scene * /*scene*/,
TreeElement * /*te*/,
TreeStoreElem * /*tsep*/,
TreeStoreElem *tselem) {
id_override_library_delete_hierarchy(&override_data, tselem);
},
OUTLINER_LIB_SELECTIONSET_SELECTED,
false);
id_override_library_delete_hierarchy_process(C, op->reports, override_data);
@ -2558,7 +2509,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
space_outliner,
&space_outliner->tree,
object_select_hierarchy_fn,
nullptr,
false);
/* FIXME: This is most certainly broken, maybe check should rather be
* `if (CTX_data_scene(C) != scene)` ? */
@ -2576,7 +2526,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
selection_changed = true;
break;
case OL_OP_REMAP:
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
break;
@ -2916,8 +2866,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_IDOP_UNLINK: {
/* unlink datablock from its parent */
if (objectlevel) {
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_object_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_object_fn);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr);
ED_undo_push(C, "Unlink Object");
@ -2926,36 +2875,32 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
switch (idlevel) {
case ID_AC:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_action_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_action_fn);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Unlink action");
break;
case ID_MA:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_material_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_material_fn);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr);
ED_undo_push(C, "Unlink material");
break;
case ID_TE:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_texture_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_texture_fn);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, nullptr);
ED_undo_push(C, "Unlink texture");
break;
case ID_WO:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_world_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_world_fn);
WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr);
ED_undo_push(C, "Unlink world");
break;
case ID_GR:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, unlink_collection_fn, nullptr);
C, op->reports, scene, space_outliner, unlink_collection_fn);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr);
ED_undo_push(C, "Unlink Collection");
@ -2968,7 +2913,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_LOCAL: {
/* make local */
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn);
ED_undo_push(C, "Localized Data");
break;
}
@ -2977,7 +2922,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
switch (idlevel) {
case ID_AC:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, singleuser_action_fn, nullptr);
C, op->reports, scene, space_outliner, singleuser_action_fn);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, nullptr);
ED_undo_push(C, "Single-User Action");
@ -2985,7 +2930,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case ID_WO:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, singleuser_world_fn, nullptr);
C, op->reports, scene, space_outliner, singleuser_world_fn);
WM_event_add_notifier(C, NC_SCENE | ND_WORLD, nullptr);
ED_undo_push(C, "Single-User World");
@ -3000,8 +2945,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
case OUTLINER_IDOP_DELETE: {
if (idlevel > 0) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_tag_fn);
BKE_id_multi_tagged_delete(bmain);
ED_undo_push(C, "Delete");
}
@ -3009,7 +2953,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_REMAP: {
if (idlevel > 0 || objectlevel) {
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
}
@ -3032,8 +2976,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_FAKE_ADD: {
/* set fake user */
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_fake_user_set_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_fake_user_set_fn);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Add Fake User");
@ -3041,8 +2984,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_FAKE_CLEAR: {
/* clear fake user */
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_fake_user_clear_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_fake_user_clear_fn);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Clear Fake User");
@ -3050,16 +2992,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_RENAME: {
/* rename */
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, item_rename_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, item_rename_fn);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
ED_undo_push(C, "Rename");
break;
}
case OUTLINER_IDOP_SELECT_LINKED:
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_select_linked_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_select_linked_fn);
ED_outliner_select_sync_from_all_tag(C);
ED_undo_push(C, "Select");
break;
@ -3143,21 +3083,19 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
switch (event) {
case OL_LIB_DELETE: {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, id_delete_tag_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_tag_fn);
BKE_id_multi_tagged_delete(bmain);
ED_undo_push(C, "Delete Library");
break;
}
case OL_LIB_RELOCATE: {
outliner_do_libdata_operation(
C, op->reports, scene, space_outliner, lib_relocate_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_relocate_fn);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
break;
}
case OL_LIB_RELOAD: {
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn, nullptr);
outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn);
/* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
* trick does not work here). */
break;

View File

@ -215,7 +215,7 @@ static const std::optional<bke::AttrDomain> convert_usd_varying_to_blender(
blender::Map<pxr::TfToken, bke::AttrDomain> map;
map.add_new(pxr::UsdGeomTokens->faceVarying, bke::AttrDomain::Corner);
map.add_new(pxr::UsdGeomTokens->vertex, bke::AttrDomain::Point);
map.add_new(pxr::UsdGeomTokens->varying, bke::AttrDomain::Corner);
map.add_new(pxr::UsdGeomTokens->varying, bke::AttrDomain::Point);
map.add_new(pxr::UsdGeomTokens->face, bke::AttrDomain::Face);
/* As there's no "constant" type in Blender, for now we're
* translating into a point Attribute. */
@ -402,7 +402,7 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh,
pxr::TfToken interp = primvar.GetInterpolation();
if ((interp == pxr::UsdGeomTokens->faceVarying && usd_colors.size() != mesh->corners_num) ||
(interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->corners_num) ||
(interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->verts_num) ||
(interp == pxr::UsdGeomTokens->vertex && usd_colors.size() != mesh->verts_num) ||
(interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) ||
(interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->faces_num))
@ -420,11 +420,7 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh,
bke::AttrDomain color_domain = bke::AttrDomain::Point;
if (ELEM(interp,
pxr::UsdGeomTokens->varying,
pxr::UsdGeomTokens->faceVarying,
pxr::UsdGeomTokens->uniform))
{
if (ELEM(interp, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->uniform)) {
color_domain = bke::AttrDomain::Corner;
}
@ -445,7 +441,7 @@ void USDMeshReader::read_color_data_primvar(Mesh *mesh,
ColorGeometry4f(usd_colors[0][0], usd_colors[0][1], usd_colors[0][2], 1.0f));
}
/* Check for situations that allow for a straight-forward copy by index. */
else if (interp == pxr::UsdGeomTokens->vertex ||
else if (interp == pxr::UsdGeomTokens->vertex || interp == pxr::UsdGeomTokens->varying ||
(interp == pxr::UsdGeomTokens->faceVarying && !is_left_handed_))
{
for (int i = 0; i < usd_colors.size(); i++) {
@ -524,7 +520,7 @@ void USDMeshReader::read_uv_data_primvar(Mesh *mesh,
if ((varying_type == pxr::UsdGeomTokens->faceVarying && usd_uvs.size() != mesh->corners_num) ||
(varying_type == pxr::UsdGeomTokens->vertex && usd_uvs.size() != mesh->verts_num) ||
(varying_type == pxr::UsdGeomTokens->varying && usd_uvs.size() != mesh->corners_num))
(varying_type == pxr::UsdGeomTokens->varying && usd_uvs.size() != mesh->verts_num))
{
BKE_reportf(reports(),
RPT_WARNING,
@ -545,7 +541,7 @@ void USDMeshReader::read_uv_data_primvar(Mesh *mesh,
return;
}
if (ELEM(varying_type, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->varying)) {
if (varying_type == pxr::UsdGeomTokens->faceVarying) {
if (is_left_handed_) {
/* Reverse the index order. */
const OffsetIndices faces = mesh->faces();

View File

@ -2602,7 +2602,7 @@ static void rna_def_brush(BlenderRNA *brna)
prop = RNA_def_property(srna, "uv_sculpt_tool", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_brush_uv_sculpt_tool_items);
RNA_def_property_ui_text(prop, "Sculpt Tool", "");
RNA_def_property_ui_text(prop, "UV Sculpt Tool", "");
RNA_def_property_update(prop, 0, "rna_Brush_update_and_reset_icon");
prop = RNA_def_property(srna, "vertex_tool", PROP_ENUM, PROP_NONE);

View File

@ -904,12 +904,6 @@ const EnumPropertyItem *RNA_node_tree_interface_socket_menu_itemf(bContext * /*C
return RNA_node_enum_definition_itemf(*data->enum_items, r_free);
}
static void rna_NodeTreeInterfaceSocket_idname_set(PointerRNA *ptr, const char *value)
{
bNodeTreeInterfaceSocket &socket = *static_cast<bNodeTreeInterfaceSocket *>(ptr->data);
socket.set_socket_type(value);
}
#else
static void rna_def_node_interface_item(BlenderRNA *brna)
@ -1063,8 +1057,6 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, nullptr, "socket_type");
RNA_def_property_flag(prop, PROP_REGISTER);
RNA_def_property_ui_text(prop, "Socket Type Name", "Name of the socket type");
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_idname_set");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
func = RNA_def_function(srna, "draw", nullptr);
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);

View File

@ -292,6 +292,79 @@ Vector<Sequence *> seq_get_shown_sequences(const Scene *scene,
return result;
}
/* Strip corner coordinates in screen pixel space. Note that they might not be
* axis aligned when rotation is present. */
struct StripScreenQuad {
float2 v0, v1, v2, v3;
bool is_empty() const
{
return v0 == v1 && v2 == v3 && v0 == v2;
}
};
static StripScreenQuad get_strip_screen_quad(const SeqRenderData *context, const Sequence *seq)
{
Scene *scene = context->scene;
const int x = context->rectx;
const int y = context->recty;
float2 offset{x * 0.5f, y * 0.5f};
float quad[4][2];
SEQ_image_transform_final_quad_get(scene, seq, quad);
return StripScreenQuad{quad[0] + offset, quad[1] + offset, quad[2] + offset, quad[3] + offset};
}
/* Is quad `a` fully contained (i.e. covered by) quad `b`? For that to happen,
* all corners of `a` have to be inside `b`. */
static bool is_quad_a_inside_b(const StripScreenQuad &a, const StripScreenQuad &b)
{
return isect_point_quad_v2(a.v0, b.v0, b.v1, b.v2, b.v3) &&
isect_point_quad_v2(a.v1, b.v0, b.v1, b.v2, b.v3) &&
isect_point_quad_v2(a.v2, b.v0, b.v1, b.v2, b.v3) &&
isect_point_quad_v2(a.v3, b.v0, b.v1, b.v2, b.v3);
}
/* Tracking of "known to be opaque" strip quad coordinates, along with their
* order index within visible strips during rendering. */
struct OpaqueQuad {
StripScreenQuad quad;
int order_index;
};
struct OpaqueQuadTracker {
Vector<OpaqueQuad, 4> opaques;
/* Determine if the input strip is completely behind opaque strips that are
* above it. Current implementation is simple and only checks if strip is
* completely covered by any other strip. It does not detect case where
* a strip is not covered by a single strip, but is behind of the union
* of the strips above. */
bool is_occluded(const SeqRenderData *context, const Sequence *seq, int order_index) const
{
StripScreenQuad quad = get_strip_screen_quad(context, seq);
if (quad.is_empty()) {
/* Strip size is not initialized/valid, we can't know if it is occluded. */
return false;
}
for (const OpaqueQuad &q : opaques) {
if (q.order_index > order_index && is_quad_a_inside_b(quad, q.quad)) {
return true;
}
}
return false;
}
void add_occluder(const SeqRenderData *context, const Sequence *seq, int order_index)
{
StripScreenQuad quad = get_strip_screen_quad(context, seq);
if (!quad.is_empty()) {
opaques.append({quad, order_index});
}
}
};
/** \} */
/* -------------------------------------------------------------------- */
@ -465,36 +538,20 @@ static void sequencer_thumbnail_transform(ImBuf *in, ImBuf *out)
* image. If they do not the image will have transparent areas. */
static bool seq_image_transform_transparency_gained(const SeqRenderData *context, Sequence *seq)
{
Scene *scene = context->scene;
const int x = context->rectx;
const int y = context->recty;
float seq_image_quad[4][2];
SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad);
for (int i = 0; i < 4; i++) {
add_v2_v2(seq_image_quad[i], float2{x / 2.0f, y / 2.0f});
float x0 = 0.0f;
float y0 = 0.0f;
float x1 = float(context->rectx);
float y1 = float(context->recty);
float x_aspect = context->scene->r.xasp / context->scene->r.yasp;
if (x_aspect != 1.0f) {
float xmid = (x0 + x1) * 0.5f;
x0 = xmid - (xmid - x0) * x_aspect;
x1 = xmid + (x1 - xmid) * x_aspect;
}
StripScreenQuad quad = get_strip_screen_quad(context, seq);
StripScreenQuad screen{float2(x0, y0), float2(x1, y0), float2(x0, y1), float2(x1, y1)};
return !isect_point_quad_v2(float2{float(x), float(y)},
seq_image_quad[0],
seq_image_quad[1],
seq_image_quad[2],
seq_image_quad[3]) ||
!isect_point_quad_v2(float2{0, float(y)},
seq_image_quad[0],
seq_image_quad[1],
seq_image_quad[2],
seq_image_quad[3]) ||
!isect_point_quad_v2(float2{float(x), 0},
seq_image_quad[0],
seq_image_quad[1],
seq_image_quad[2],
seq_image_quad[3]) ||
!isect_point_quad_v2(float2{0, 0},
seq_image_quad[0],
seq_image_quad[1],
seq_image_quad[2],
seq_image_quad[3]);
return !is_quad_a_inside_b(screen, quad);
}
/* Automatic filter:
@ -709,8 +766,10 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context,
use_preprocess = true;
}
/* Proxies and effect strips are not stored in cache. */
if (!is_proxy_image && (seq->type & SEQ_TYPE_EFFECT) == 0) {
/* Proxies and non-generator effect strips are not stored in cache. */
const bool is_effect_with_inputs = (seq->type & SEQ_TYPE_EFFECT) != 0 &&
SEQ_effect_get_num_inputs(seq->type) != 0;
if (!is_proxy_image && !is_effect_with_inputs) {
seq_cache_put(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW, ibuf);
}
@ -1900,6 +1959,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
return nullptr;
}
OpaqueQuadTracker opaques;
int64_t i;
ImBuf *out = nullptr;
for (i = strips.size() - 1; i >= 0; i--) {
@ -1917,9 +1978,15 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
StripEarlyOut early_out = seq_get_early_out_for_blend_mode(seq);
if (early_out == StripEarlyOut::DoEffect && opaques.is_occluded(context, seq, i)) {
early_out = StripEarlyOut::UseInput1;
}
/* Early out for alpha over. It requires image to be rendered, so it can't use
* `seq_get_early_out_for_blend_mode`. */
if (out == nullptr && seq->blend_mode == SEQ_TYPE_ALPHAOVER && seq->blend_opacity == 100.0f) {
if (out == nullptr && seq->blend_mode == SEQ_TYPE_ALPHAOVER &&
early_out == StripEarlyOut::DoEffect && seq->blend_opacity == 100.0f)
{
ImBuf *test = seq_render_strip(context, state, seq, timeline_frame);
if (ELEM(test->planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB)) {
early_out = StripEarlyOut::UseInput2;
@ -1929,6 +1996,16 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
}
/* Free the image. It is stored in cache, so this doesn't affect performance. */
IMB_freeImBuf(test);
/* Check whether the raw (before preprocessing, which can add alpha) strip content
* was opaque. */
ImBuf *ibuf_raw = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW);
if (ibuf_raw != nullptr) {
if (ibuf_raw->planes != R_IMF_PLANES_RGBA) {
opaques.add_occluder(context, seq, i);
}
IMB_freeImBuf(ibuf_raw);
}
}
switch (early_out) {
@ -1966,6 +2043,10 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
for (; i < strips.size(); i++) {
Sequence *seq = strips[i];
if (opaques.is_occluded(context, seq, i)) {
continue;
}
if (seq_get_early_out_for_blend_mode(seq) == StripEarlyOut::DoEffect) {
ImBuf *ibuf1 = out;
ImBuf *ibuf2 = seq_render_strip(context, state, seq, timeline_frame);

View File

@ -1,4 +1,4 @@
/* SPDX-FileCopyrightText: 2004 Blender Authors
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */

View File

@ -676,7 +676,7 @@ static void seq_image_transform_quad_get_ex(const Scene *scene,
float quad_temp[4][3];
for (int i = 0; i < 4; i++) {
zero_v2(quad_temp[i]);
zero_v3(quad_temp[i]);
}
quad_temp[0][0] = (image_size[0] / 2) - crop->right;