WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 351 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
23 changed files with 563 additions and 564 deletions
Showing only changes of commit 50e4d3820f - 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;