diff options
Diffstat (limited to 'source/blender')
109 files changed, 1852 insertions, 1093 deletions
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 27478bd7f8e..a2c778fcf16 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -34,6 +34,7 @@ #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_ADVANCES_H /* For FT_Get_Advance */ #include "MEM_guardedalloc.h" @@ -297,44 +298,27 @@ static void blf_batch_draw_end(void) * characters. */ -BLI_INLINE GlyphBLF *blf_utf8_next_fast( - FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p, uint *r_c) +BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step( + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { - GlyphBLF *g; - if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) { - g = (gc->glyph_ascii_table)[*r_c]; - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); - gc->glyph_ascii_table[*r_c] = g; - } - (*i_p)++; - } - else { - *r_c = BLI_str_utf8_as_unicode_step(str, str_len, i_p); - g = blf_glyph_search(gc, *r_c); - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); - } - } - return g; + uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); + /* Invalid unicode sequences return the byte value, stepping forward one. + * This allows `latin1` to display (which is sometimes used for file-paths). */ + BLI_assert(charcode != BLI_UTF8_ERR); + return blf_glyph_ensure(font, gc, charcode); } -BLI_INLINE void blf_kerning_step_fast(FontBLF *font, - const GlyphBLF *g_prev, - const GlyphBLF *g, - const uint c_prev, - const uint c, - int *pen_x_p) +BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) { if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { - return; + return 0; } FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - delta.x = font->kerning_cache->ascii_table[c][c_prev]; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; } /* If not ASCII or not found in cache, ask FreeType for kerning. */ @@ -344,14 +328,16 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } /* If ASCII we save this value to our cache for quicker access next time. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; } if (delta.x != 0) { /* Convert unscaled design units to pixels and move pen. */ - *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); + return blf_unscaled_F26Dot6_to_pixels(font, delta.x); } + + return 0; } /** \} */ @@ -367,7 +353,6 @@ static void blf_font_draw_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -380,22 +365,18 @@ static void blf_font_draw_ex(FontBLF *font, blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; - c_prev = c; } blf_batch_draw_end(); @@ -415,7 +396,6 @@ void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct /* use fixed column width, but an utf8 character may occupy multiple columns */ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth) { - unsigned int c; GlyphBLF *g; int col, columns = 0; int pen_x = 0, pen_y = 0; @@ -426,19 +406,15 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); - col = BLI_wcwidth((char32_t)c); + col = BLI_wcwidth((char32_t)g->c); if (col < 0) { col = 1; } @@ -467,7 +443,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = (int)font->pos[0]; int pen_y_basis = (int)font->pos[1] + pen_y; @@ -483,15 +458,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* another buffer specific call for color conversion */ while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -588,7 +560,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (r_info) { @@ -617,31 +588,22 @@ void blf_font_draw_buffer(FontBLF *font, * - #BLF_width_to_rstrlen * \{ */ -static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const uint c_prev, - const uint c, - GlyphBLF *g_prev, - GlyphBLF *g, - int *pen_x, - const int width_i) +static bool blf_font_width_to_strlen_glyph_process( + FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i) { - if (UNLIKELY(c == BLI_UTF8_ERR)) { - return true; /* break the calling loop. */ - } if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); - + *pen_x += blf_kerning(font, g_prev, g); *pen_x += g->advance_i; + /* When true, break the calling loop. */ return (*pen_x >= width_i); } size_t blf_font_width_to_strlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev; @@ -649,11 +611,11 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i]; - i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL; (i < str_len) && str[i]; + i_prev = i, width_new = pen_x, g_prev = g) { + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -669,7 +631,6 @@ size_t blf_font_width_to_strlen( size_t blf_font_width_to_rstrlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev, i_tmp; @@ -685,19 +646,19 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)(s_prev - str); i_tmp = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); for (width_new = pen_x = 0; (s != NULL); - i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { + i = i_prev, s = s_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(s, str); i_prev = (size_t)(s_prev - str); if (s_prev != NULL) { i_tmp = i_prev; - g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c_prev); + g_prev = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -724,7 +685,6 @@ static void blf_font_boundbox_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -736,15 +696,12 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymax = -32000.0f; while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -767,7 +724,6 @@ static void blf_font_boundbox_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (box->xmin > box->xmax) { @@ -869,22 +825,7 @@ float blf_font_height(FontBLF *font, float blf_font_fixed_width(FontBLF *font) { - const unsigned int c = ' '; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } - - blf_glyph_cache_release(font); - return g->advance; + return (float)font->fixed_width; } static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, @@ -896,7 +837,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0, i_curr; @@ -909,15 +849,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, while ((i < str_len) && str[i]) { i_curr = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = pen_x; gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); @@ -931,7 +868,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } g_prev = g; - c_prev = c; } if (r_info) { @@ -978,7 +914,6 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0, pen_y = 0; size_t i = 0; @@ -999,15 +934,12 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); /** * Implementation Detail (utf8). @@ -1045,16 +977,14 @@ static void blf_font_wrap_apply(FontBLF *font, wrap.start = wrap.last[0]; i = wrap.last[1]; pen_x = 0; - pen_y -= gc->glyph_height_max; + pen_y -= blf_font_height_max(font); g_prev = NULL; - c_prev = BLI_UTF8_ERR; lines += 1; continue; } pen_x = pen_x_next; g_prev = g; - c_prev = c; } // printf("done! lines: %d, width, %d\n", lines, pen_x_next); @@ -1170,45 +1100,41 @@ int blf_font_count_missing_chars(FontBLF *font, int blf_font_height_max(FontBLF *font) { int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - height_max = gc->glyph_height_max; - - blf_glyph_cache_release(font); - return height_max; + if (FT_IS_SCALABLE(font->face)) { + height_max = (int)((float)(font->face->ascender - font->face->descender) * + (((float)font->face->size->metrics.y_ppem) / + ((float)font->face->units_per_EM))); + } + else { + height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(height_max, 1); } int blf_font_width_max(FontBLF *font) { int width_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - width_max = gc->glyph_width_max; - - blf_glyph_cache_release(font); - return width_max; + if (FT_IS_SCALABLE(font->face)) { + width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * + (((float)font->face->size->metrics.x_ppem) / + ((float)font->face->units_per_EM))); + } + else { + width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(width_max, 1); } float blf_font_descender(FontBLF *font) { - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; - - blf_glyph_cache_release(font); - return descender; + return ((float)font->face->size->metrics.descender) / 64.0f; } float blf_font_ascender(FontBLF *font) { - float ascender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; - - blf_glyph_cache_release(font); - return ascender; + return ((float)font->face->size->metrics.ascender) / 64.0f; } char *blf_display_name(FontBLF *font) @@ -1439,6 +1365,22 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) } blf_glyph_cache_release(font); + + /* Set fixed-width size for monospaced output. */ + FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); + if (gindex) { + FT_Fixed advance = 0; + FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); + /* Use CSS 'ch unit' width, advance of zero character. */ + font->fixed_width = (int)(advance >> 16); + } + else { + /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ + font->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + } + if (font->fixed_width < 1) { + font->fixed_width = 1; + } } /** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 6cdf5fc5996..c4ffb3f87f1 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -86,27 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; - gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; - - if (FT_IS_SCALABLE(font->face)) { - gc->glyph_width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * - (((float)font->face->size->metrics.x_ppem) / - ((float)font->face->units_per_EM))); - - gc->glyph_height_max = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) * - (((float)font->face->size->metrics.y_ppem) / - ((float)font->face->units_per_EM))); - } - else { - gc->glyph_width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); - gc->glyph_height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); - } - - /* can happen with size 1 fonts */ - CLAMP_MIN(gc->glyph_width_max, 1); - CLAMP_MIN(gc->glyph_height_max, 1); - BLI_addhead(&font->cache, gc); return gc; } @@ -159,7 +138,7 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc) MEM_freeN(gc); } -GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) +static GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) { GlyphBLF *p; unsigned int key; @@ -175,33 +154,8 @@ GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) return NULL; } -GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, unsigned int c) +static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index) { - FT_GlyphSlot slot; - GlyphBLF *g; - FT_Error err; - FT_Bitmap bitmap, tempbitmap; - FT_BBox bbox; - unsigned int key; - - g = blf_glyph_search(gc, c); - if (g) { - return g; - } - - /* glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally */ - BLI_spin_lock(font->ft_lib_mutex); - - /* search again after locking */ - g = blf_glyph_search(gc, c); - if (g) { - BLI_spin_unlock(font->ft_lib_mutex); - return g; - } - int load_flags; int render_mode; @@ -228,7 +182,10 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - err = FT_Load_Glyph(font->face, (FT_UInt)index, load_flags); + FT_Error err = FT_Load_Glyph(font->face, glyph_index, load_flags); + if (err != 0) { + return false; + } /* Do not oblique a font that is designed to be italic! */ if (((font->flags & BLF_ITALIC) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_ITALIC) && @@ -243,9 +200,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } /* Do not embolden an already bold font! */ - if (((font->flags & BLF_BOLD) != 0) && - !(font->face->style_flags & FT_STYLE_FLAG_BOLD) & - (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { + if (((font->flags & BLF_BOLD) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_BOLD) && + (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { /* Strengthen the width more than the height. */ const FT_Pos extra_x = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.x_scale) / 14; @@ -263,15 +219,12 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - if (err) { - BLI_spin_unlock(font->ft_lib_mutex); - return NULL; - } - /* get the glyph. */ - slot = font->face->glyph; + FT_GlyphSlot slot = font->face->glyph; err = FT_Render_Glyph(slot, render_mode); + FT_Bitmap tempbitmap; + if (font->flags & BLF_MONOCHROME) { /* Convert result from 1 bit per pixel to 8 bit per pixel */ /* Accum errors for later, fine if not interested beyond "ok vs any error" */ @@ -284,45 +237,69 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } if (err || slot->format != FT_GLYPH_FORMAT_BITMAP) { - BLI_spin_unlock(font->ft_lib_mutex); - return NULL; + return false; } - g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add"); - g->c = c; - g->idx = (FT_UInt)index; - bitmap = slot->bitmap; - g->dims[0] = (int)bitmap.width; - g->dims[1] = (int)bitmap.rows; + return true; +} - const int buffer_size = g->dims[0] * g->dims[1]; +GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) +{ + GlyphBLF *g = (charcode < GLYPH_ASCII_TABLE_SIZE) ? (gc->glyph_ascii_table)[charcode] : + blf_glyph_search(gc, charcode); + if (g) { + return g; + } - if (buffer_size != 0) { - if (font->flags & BLF_MONOCHROME) { - /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ - for (int i = 0; i < buffer_size; i++) { - bitmap.buffer[i] = bitmap.buffer[i] ? 255 : 0; - } - } + FT_UInt glyph_index = FT_Get_Char_Index(font->face, charcode); - g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); - memcpy(g->bitmap, bitmap.buffer, (size_t)buffer_size); + if (!blf_glyph_render(font, glyph_index)) { + return NULL; } + FT_GlyphSlot slot = font->face->glyph; + + /* glyphs are dynamically created as needed by font rendering. this means that + * to make font rendering thread safe we have to do locking here. note that this + * must be a lock for the whole library and not just per font, because the font + * renderer uses a shared buffer internally */ + BLI_spin_lock(font->ft_lib_mutex); + + g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get"); + g->c = charcode; + g->idx = glyph_index; g->advance = ((float)slot->advance.x) / 64.0f; g->advance_i = (int)g->advance; g->pos[0] = slot->bitmap_left; g->pos[1] = slot->bitmap_top; + g->dims[0] = (int)slot->bitmap.width; + g->dims[1] = (int)slot->bitmap.rows; g->pitch = slot->bitmap.pitch; + FT_BBox bbox; FT_Outline_Get_CBox(&(slot->outline), &bbox); g->box.xmin = ((float)bbox.xMin) / 64.0f; g->box.xmax = ((float)bbox.xMax) / 64.0f; g->box.ymin = ((float)bbox.yMin) / 64.0f; g->box.ymax = ((float)bbox.yMax) / 64.0f; - key = blf_hash(g->c); + const int buffer_size = (int)(slot->bitmap.width * slot->bitmap.rows); + if (buffer_size != 0) { + if (font->flags & BLF_MONOCHROME) { + /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ + for (int i = 0; i < buffer_size; i++) { + slot->bitmap.buffer[i] = slot->bitmap.buffer[i] ? 255 : 0; + } + } + g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); + memcpy(g->bitmap, slot->bitmap.buffer, (size_t)buffer_size); + } + + unsigned int key = blf_hash(g->c); BLI_addhead(&(gc->bucket[key]), g); + if (charcode < GLYPH_ASCII_TABLE_SIZE) { + gc->glyph_ascii_table[charcode] = g; + } BLI_spin_unlock(font->ft_lib_mutex); @@ -419,7 +396,7 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y); } -void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) +void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) { if ((!g->dims[0]) || (!g->dims[1])) { return; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 6fd5e8b7503..a715d5df692 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -139,14 +139,10 @@ void blf_glyph_cache_release(struct FontBLF *font); void blf_glyph_cache_clear(struct FontBLF *font); void blf_glyph_cache_free(struct GlyphCacheBLF *gc); -struct GlyphBLF *blf_glyph_search(struct GlyphCacheBLF *gc, unsigned int c); -struct GlyphBLF *blf_glyph_add(struct FontBLF *font, - struct GlyphCacheBLF *gc, - unsigned int index, - unsigned int c); +struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc, uint charcode); void blf_glyph_free(struct GlyphBLF *g); -void blf_glyph_render( +void blf_glyph_draw( struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y); #ifdef WIN32 diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index e90f82da7f3..aae666fa182 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -86,13 +86,6 @@ typedef struct GlyphCacheBLF { int bitmap_len_landed; int bitmap_len_alloc; - /* and the bigger glyph in the font. */ - int glyph_width_max; - int glyph_height_max; - - /* ascender and descender value. */ - float ascender; - float descender; } GlyphCacheBLF; typedef struct GlyphBLF { @@ -214,6 +207,9 @@ typedef struct FontBLF { /* font size. */ unsigned int size; + /* Column width when printing monospaced. */ + int fixed_width; + /* max texture size. */ int tex_size_max; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 3153a55b697..12a83f7634e 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -106,7 +106,7 @@ void BLF_thumb_preview(const char *filename, font_size_curr -= (font_size_curr / font_shrink); font_shrink += 1; - font->pos[1] -= gc->ascender * 1.1f; + font->pos[1] -= blf_font_ascender(font) * 1.1f; /* We fallback to default english strings in case not enough chars are available in current * font for given translated string (useful in non-latin i18n context, like Chinese, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 115de3334b2..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,15 +31,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 300 +#define BLENDER_VERSION 301 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 40 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 7a8c55071df..77f1d197844 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -47,9 +47,6 @@ struct anim; #define IMA_MAX_SPACE 64 #define IMA_UDIM_MAX 2000 -void BKE_images_init(void); -void BKE_images_exit(void); - void BKE_image_free_packedfiles(struct Image *image); void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 957a623577e..30c742e3af6 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -143,7 +143,8 @@ enum { typedef struct LibraryForeachIDData LibraryForeachIDData; -bool BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, +bool BKE_lib_query_foreachid_iter_stop(struct LibraryForeachIDData *data); +void BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, struct ID **id_pp, int cb_flag); int BKE_lib_query_foreachid_process_flags_get(struct LibraryForeachIDData *data); @@ -154,7 +155,8 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach #define BKE_LIB_FOREACHID_PROCESS_ID(_data, _id, _cb_flag) \ { \ CHECK_TYPE_ANY((_id), ID *, void *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ @@ -163,13 +165,23 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach #define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ ((void)0) -bool BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); +#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(_data, _func_call) \ + { \ + _func_call; \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ + return; \ + } \ + } \ + ((void)0) + +void BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); void BKE_lib_query_idpropertiesForeachIDLink_callback(struct IDProperty *id_prop, void *user_data); /* Loop over all of the ID's this datablock links to. */ diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index c70521f9593..5e154459a6c 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -89,6 +89,12 @@ enum { * dealing with IDs temporarily out of Main, but which will be put in it ultimately). */ ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, + /** + * Force obdata pointers to also be processed, even when object (`id_owner`) is in Edit mode. + * This is required by some tools creating/deleting IDs while operating in Edit mode, like e.g. + * the 'separate' mesh operator. + */ + ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9, }; /* NOTE: Requiring new_id to be non-null, this *may* not be the case ultimately, @@ -111,8 +117,8 @@ void BKE_libblock_relink_ex(struct Main *bmain, void *new_idv, const short remap_flags) ATTR_NONNULL(1, 2); -void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL(); -void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, const int remap_flag) + ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 7403b7f2109..cae72ddf68c 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -175,7 +175,7 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) bAction *act = (bAction *)id; LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 23d2c4fe55f..21887d514d9 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -294,7 +294,7 @@ bool BKE_animdata_id_is_animated(const struct ID *id) void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data) { LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 08a3b7d0bbb..d872dc67dcb 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -279,23 +279,31 @@ bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) /** * Gets a good default directory for fonts. */ -bool BKE_appdir_font_folder_default( - /* This parameter can only be `const` on non-windows platforms. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - char *dir) +bool BKE_appdir_font_folder_default(char *dir) { - bool success = false; + char test_dir[FILE_MAXDIR]; + test_dir[0] = '\0'; + #ifdef WIN32 wchar_t wpath[FILE_MAXDIR]; - success = SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0); - if (success) { + if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) { wcscat(wpath, L"\\"); - BLI_strncpy_wchar_as_utf8(dir, wpath, FILE_MAXDIR); + BLI_strncpy_wchar_as_utf8(test_dir, wpath, sizeof(test_dir)); + } +#elif defined(__APPLE__) + const char *home = BLI_getenv("HOME"); + if (home) { + BLI_path_join(test_dir, sizeof(test_dir), home, "Library", "Fonts", NULL); } +#else + STRNCPY(test_dir, "/usr/share/fonts"); #endif - /* TODO: Values for other platforms. */ - UNUSED_VARS(dir); - return success; + + if (test_dir[0] && BLI_exists(test_dir)) { + BLI_strncpy(dir, test_dir, FILE_MAXDIR); + return true; + } + return false; } /** \} */ diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index b64b050f4e7..b830c9de5f5 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -161,30 +161,36 @@ static void armature_free_data(struct ID *id) static void armature_foreach_id_bone(Bone *bone, LibraryForeachIDData *data) { - IDP_foreach_property( - bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { - armature_foreach_id_bone(curbone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(curbone, data)); } } static void armature_foreach_id_editbone(EditBone *edit_bone, LibraryForeachIDData *data) { - IDP_foreach_property( - edit_bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(edit_bone->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } static void armature_foreach_id(ID *id, LibraryForeachIDData *data) { bArmature *arm = (bArmature *)id; LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { - armature_foreach_id_bone(bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(bone, data)); } if (arm->edbo != NULL) { LISTBASE_FOREACH (EditBone *, edit_bone, arm->edbo) { - armature_foreach_id_editbone(edit_bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_editbone(edit_bone, data)); } } } diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index 7bea089b9bf..59e402b6680 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -21,14 +21,12 @@ #include <cstring> #include "DNA_ID.h" -#include "DNA_asset_types.h" #include "DNA_defaults.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_string_ref.hh" #include "BLI_string_utils.h" -#include "BLI_utildefines.h" #include "BLI_uuid.h" #include "BKE_asset.h" diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 13f66445c46..03043f3b784 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -18,25 +18,20 @@ * \ingroup bke */ +#include <fstream> +#include <set> + #include "BKE_asset_catalog.hh" #include "BKE_asset_library.h" -#include "BKE_preferences.h" #include "BLI_fileops.h" #include "BLI_path_util.h" -#include "BLI_set.hh" -#include "BLI_string_ref.hh" - -#include "DNA_userdef_types.h" /* For S_ISREG() and S_ISDIR() on Windows. */ #ifdef WIN32 # include "BLI_winstuff.h" #endif -#include <fstream> -#include <set> - namespace blender::bke { const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc index aae8a289d32..68e43852a21 100644 --- a/source/blender/blenkernel/intern/asset_library.cc +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -18,9 +18,9 @@ * \ingroup bke */ -#include "BKE_asset_catalog.hh" +#include <memory> + #include "BKE_asset_library.hh" -#include "BKE_callbacks.h" #include "BKE_main.h" #include "BKE_preferences.h" @@ -29,12 +29,8 @@ #include "DNA_asset_types.h" #include "DNA_userdef_types.h" -#include "MEM_guardedalloc.h" - #include "asset_library_service.hh" -#include <memory> - bool blender::bke::AssetLibrary::save_catalogs_when_file_is_saved = true; /** diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc index 7cf95ee4cc1..d202d6462cf 100644 --- a/source/blender/blenkernel/intern/asset_library_service.cc +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -20,16 +20,12 @@ #include "asset_library_service.hh" -#include "BKE_asset_library.hh" #include "BKE_blender.h" -#include "BKE_callbacks.h" -#include "BLI_fileops.h" +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ #include "BLI_path_util.h" #include "BLI_string_ref.hh" -#include "MEM_guardedalloc.h" - #include "CLG_log.h" static CLG_LogRef LOG = {"bke.asset_service"}; diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index e26ae05301e..ee910cab945 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -19,7 +19,7 @@ #include "asset_library_service.hh" -#include "BLI_fileops.h" +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ #include "BLI_path_util.h" #include "BKE_appdir.h" diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 97f8bddc043..fb65a9bec7e 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -90,7 +90,6 @@ void BKE_blender_free(void) IMB_exit(); BKE_cachefiles_exit(); - BKE_images_exit(); DEG_free_node_types(); BKE_brush_system_exit(); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 7d217d6907d..dc3c2a8e55e 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -213,8 +213,9 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) if (brush->gpencil_settings) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER); } - BKE_texture_mtex_foreach_id(data, &brush->mtex); - BKE_texture_mtex_foreach_id(data, &brush->mask_mtex); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, &brush->mask_mtex)); } static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 2dca5dcb75d..22b939d3cf9 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -716,7 +716,7 @@ Collection *BKE_collection_duplicate(Main *bmain, collection_new->id.tag &= ~LIB_TAG_NEW; /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&collection_new->id); + BKE_libblock_relink_to_newid(bmain, &collection_new->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1564eb3aa7b..bbf61c51bfb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -205,12 +205,16 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) FMod_Python *fcm_py = (FMod_Python *)fcm->data; BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP); - IDP_foreach_property(fcm_py->prop, - IDP_TYPE_FILTER_ID, - BKE_lib_query_idpropertiesForeachIDLink_callback, - data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(fcm_py->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); break; } + default: + break; } } } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index cd1bafe445a..c250c14f1d7 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -610,24 +610,32 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) if (geometry_set == nullptr) { return false; } - if (geometry_set->has_instances()) { - return true; - } - const bool has_mesh = geometry_set->has_mesh(); - const bool has_pointcloud = geometry_set->has_pointcloud(); - const bool has_volume = geometry_set->has_volume(); - const bool has_curve = geometry_set->has_curve(); - if (ob->type == OB_MESH) { - return has_pointcloud || has_volume || has_curve; - } - if (ob->type == OB_POINTCLOUD) { - return has_mesh || has_volume || has_curve; - } - if (ob->type == OB_VOLUME) { - return has_mesh || has_pointcloud || has_curve; - } - if (ELEM(ob->type, OB_CURVE, OB_FONT)) { - return has_mesh || has_pointcloud || has_volume; + for (const GeometryComponent *component : geometry_set->get_components_for_read()) { + if (component->is_empty()) { + continue; + } + const GeometryComponentType type = component->type(); + bool is_instance = false; + switch (type) { + case GEO_COMPONENT_TYPE_MESH: + is_instance = ob->type != OB_MESH; + break; + case GEO_COMPONENT_TYPE_POINT_CLOUD: + is_instance = ob->type != OB_POINTCLOUD; + break; + case GEO_COMPONENT_TYPE_INSTANCES: + is_instance = true; + break; + case GEO_COMPONENT_TYPE_VOLUME: + is_instance = ob->type != OB_VOLUME; + break; + case GEO_COMPONENT_TYPE_CURVE: + is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT); + break; + } + if (is_instance) { + return true; + } } return false; } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index c9ac1f32804..8472ad5d8aa 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -112,12 +112,26 @@ #include "DNA_view3d_types.h" static CLG_LogRef LOG = {"bke.image"}; -static ThreadMutex *image_mutex; static void image_init(Image *ima, short source, short type); static void image_free_packedfiles(Image *ima); static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); +/* Reset runtime image fields when datablock is being initialized. */ +static void image_runtime_reset(struct Image *image) +{ + memset(&image->runtime, 0, sizeof(image->runtime)); + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + +/* Reset runtime image fields when datablock is being copied. */ +static void image_runtime_reset_on_copy(struct Image *image) +{ + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + static void image_init_data(ID *id) { Image *image = (Image *)id; @@ -167,6 +181,8 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c else { image_dst->preview = NULL; } + + image_runtime_reset_on_copy(image_dst); } static void image_free_data(ID *id) @@ -194,6 +210,9 @@ static void image_free_data(ID *id) BLI_freelistN(&image->tiles); BLI_freelistN(&image->gpu_refresh_areas); + + BLI_mutex_end(image->runtime.cache_mutex); + MEM_freeN(image->runtime.cache_mutex); } static void image_foreach_cache(ID *id, @@ -325,6 +344,8 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); + + image_runtime_reset(ima); } static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id) @@ -452,16 +473,6 @@ static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_e return NULL; } -void BKE_images_init(void) -{ - image_mutex = BLI_mutex_alloc(); -} - -void BKE_images_exit(void) -{ - BLI_mutex_free(image_mutex); -} - /* ***************** ALLOC & FREE, DATA MANAGING *************** */ static void image_free_cached_frames(Image *image) @@ -514,7 +525,7 @@ static void image_free_anims(Image *ima) void BKE_image_free_buffers_ex(Image *ima, bool do_lock) { if (do_lock) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); } image_free_cached_frames(ima); @@ -528,7 +539,7 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) BKE_image_free_gputextures(ima); if (do_lock) { - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -567,6 +578,8 @@ static void image_init(Image *ima, short source, short type) } } + image_runtime_reset(ima); + BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } @@ -640,7 +653,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) { /* sanity check */ if (dest && source && dest != source) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(source->runtime.cache_mutex); + BLI_mutex_lock(dest->runtime.cache_mutex); + if (source->cache != NULL) { struct MovieCacheIter *iter; iter = IMB_moviecacheIter_new(source->cache); @@ -652,7 +667,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(dest->runtime.cache_mutex); + BLI_mutex_unlock(source->runtime.cache_mutex); BKE_id_free(bmain, source); } @@ -1243,7 +1260,8 @@ static uintptr_t image_mem_size(Image *image) return 0; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); + if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -1277,7 +1295,8 @@ static uintptr_t image_mem_size(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(image->runtime.cache_mutex); return size; } @@ -1355,11 +1374,11 @@ static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void /* except_frame is weak, only works for seqs without offset... */ void BKE_image_free_anim_ibufs(Image *ima, int except_frame) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); if (ima->cache != NULL) { IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) @@ -3278,7 +3297,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * } if (do_reset) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); image_free_cached_frames(ima); BKE_image_free_views(ima); @@ -3286,7 +3305,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * /* add new views */ image_viewer_create_views(rd, ima); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } BLI_thread_unlock(LOCK_DRAW_IMAGE); @@ -3550,7 +3569,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) return; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); switch (signal) { case IMA_SIGNAL_FREE: @@ -3670,7 +3689,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); /* don't use notifiers because they are not 100% sure to succeeded * this also makes sure all scenes are accounted for. */ @@ -5095,11 +5114,11 @@ ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) { ImBuf *ibuf; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); ibuf = image_acquire_ibuf(ima, iuser, r_lock); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); return ibuf; } @@ -5118,9 +5137,9 @@ void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock) } if (ibuf) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -5134,7 +5153,7 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) return false; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL, NULL); @@ -5142,7 +5161,7 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) ibuf = image_acquire_ibuf(ima, iuser, NULL); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); @@ -5162,6 +5181,7 @@ typedef struct ImagePoolItem { typedef struct ImagePool { ListBase image_buffers; BLI_mempool *memory_pool; + ThreadMutex mutex; } ImagePool; ImagePool *BKE_image_pool_new(void) @@ -5169,21 +5189,28 @@ ImagePool *BKE_image_pool_new(void) ImagePool *pool = MEM_callocN(sizeof(ImagePool), "Image Pool"); pool->memory_pool = BLI_mempool_create(sizeof(ImagePoolItem), 0, 128, BLI_MEMPOOL_NOP); + BLI_mutex_init(&pool->mutex); + return pool; } void BKE_image_pool_free(ImagePool *pool) { /* Use single lock to dereference all the image buffers. */ - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(&pool->mutex); for (ImagePoolItem *item = pool->image_buffers.first; item != NULL; item = item->next) { if (item->ibuf != NULL) { + BLI_mutex_lock(item->image->runtime.cache_mutex); IMB_freeImBuf(item->ibuf); + BLI_mutex_unlock(item->image->runtime.cache_mutex); } } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); BLI_mempool_destroy(pool->memory_pool); + + BLI_mutex_end(&pool->mutex); + MEM_freeN(pool); } @@ -5215,28 +5242,34 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool } if (pool == NULL) { - /* pool could be NULL, in this case use general acquire function */ + /* Pool could be NULL, in this case use general acquire function. */ return BKE_image_acquire_ibuf(ima, iuser, NULL); } image_get_entry_and_index(ima, iuser, &entry, &index); + /* Use double-checked locking, to avoid locking when the requested image buffer is already in the + * pool. */ + ibuf = image_pool_find_item(pool, ima, entry, index, &found); if (found) { return ibuf; } - BLI_mutex_lock(image_mutex); + /* Lock the pool, to allow thread-safe modification of the content of the pool. */ + BLI_mutex_lock(&pool->mutex); ibuf = image_pool_find_item(pool, ima, entry, index, &found); - /* will also create item even in cases image buffer failed to load, - * prevents trying to load the same buggy file multiple times - */ + /* Will also create item even in cases image buffer failed to load, + * prevents trying to load the same buggy file multiple times. */ if (!found) { ImagePoolItem *item; - ibuf = image_acquire_ibuf(ima, iuser, NULL); + /* Thread-safe acquisition of an image buffer from the image. + * The acquisition does not use image pools, so there is no risk of recursive or out-of-order + * mutex locking. */ + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); item = BLI_mempool_alloc(pool->memory_pool); item->image = ima; @@ -5247,7 +5280,7 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool BLI_addtail(&pool->image_buffers, item); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); return ibuf; } @@ -5636,7 +5669,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) bool is_dirty = false; bool is_writable = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5651,7 +5684,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); if (r_is_writable) { *r_is_writable = is_writable; @@ -5680,7 +5713,7 @@ bool BKE_image_buffer_format_writable(ImBuf *ibuf) void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5694,14 +5727,14 @@ void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions * } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); } bool BKE_image_has_loaded_ibuf(Image *image) { bool has_loaded_ibuf = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5711,7 +5744,7 @@ bool BKE_image_has_loaded_ibuf(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return has_loaded_ibuf; } @@ -5724,7 +5757,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5739,7 +5772,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } @@ -5757,7 +5790,7 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5770,7 +5803,7 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 4165452801c..74750a9b61a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -76,48 +76,52 @@ typedef struct LibraryForeachIDData { BLI_LINKSTACK_DECLARE(ids_todo, ID *); } LibraryForeachIDData; -bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) +/** Check whether current iteration over ID usages should be stopped or not. + * \return true if the iteration should be stopped, false otherwise. */ +bool BKE_lib_query_foreachid_iter_stop(LibraryForeachIDData *data) { - if (!(data->status & IDWALK_STOP)) { - const int flag = data->flag; - ID *old_id = *id_pp; - - /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic - * caller code. */ - cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); - - /* Update the callback flags with some extra information regarding overrides: all 'loopback', - * 'internal', 'embedded' etc. ID pointers are never overridable. */ - if (cb_flag & - (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { - cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; - } + return (data->status & IDWALK_STOP) != 0; +} - const int callback_return = data->callback( - &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, - .bmain = data->bmain, - .id_owner = data->owner_id, - .id_self = data->self_id, - .id_pointer = id_pp, - .cb_flag = cb_flag}); - if (flag & IDWALK_READONLY) { - BLI_assert(*(id_pp) == old_id); - } - if (old_id && (flag & IDWALK_RECURSE)) { - if (BLI_gset_add((data)->ids_handled, old_id)) { - if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { - BLI_LINKSTACK_PUSH(data->ids_todo, old_id); - } +void BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) +{ + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; + } + + const int flag = data->flag; + ID *old_id = *id_pp; + + /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic + * caller code. */ + cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); + + /* Update the callback flags with some extra information regarding overrides: all 'loopback', + * 'internal', 'embedded' etc. ID pointers are never overridable. */ + if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { + cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; + } + + const int callback_return = data->callback( + &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, + .bmain = data->bmain, + .id_owner = data->owner_id, + .id_self = data->self_id, + .id_pointer = id_pp, + .cb_flag = cb_flag}); + if (flag & IDWALK_READONLY) { + BLI_assert(*(id_pp) == old_id); + } + if (old_id && (flag & IDWALK_RECURSE)) { + if (BLI_gset_add((data)->ids_handled, old_id)) { + if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { + BLI_LINKSTACK_PUSH(data->ids_todo, old_id); } } - if (callback_return & IDWALK_RET_STOP_ITER) { - data->status |= IDWALK_STOP; - return false; - } - return true; } - - return false; + if (callback_return & IDWALK_RET_STOP_ITER) { + data->status |= IDWALK_STOP; + } } int BKE_lib_query_foreachid_process_flags_get(LibraryForeachIDData *data) @@ -139,7 +143,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(LibraryForeachIDData return cb_flag_backup; } -static void library_foreach_ID_link(Main *bmain, +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -158,19 +162,24 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag); } -bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) +/** Process embedded ID pointers (root nodetrees, master collections, ...). + * + * Those require specific care, since they are technically sub-data of their owner, yet in some + * cases they still behave as regular IDs. */ +void BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) { /* Needed e.g. for callbacks handling relationships. This call shall be absolutely read-only. */ ID *id = *id_pp; const int flag = data->flag; - if (!BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED)) { - return false; + BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; } BLI_assert(id == *id_pp); if (id == NULL) { - return true; + return; } if (flag & IDWALK_IGNORE_EMBEDDED_ID) { @@ -186,14 +195,24 @@ bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) } } else { - library_foreach_ID_link( - data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data); + if (!library_foreach_ID_link( + data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data)) { + data->status |= IDWALK_STOP; + return; + } } +} - return true; +static void library_foreach_ID_data_cleanup(LibraryForeachIDData *data) +{ + if (data->ids_handled != NULL) { + BLI_gset_free(data->ids_handled, NULL); + BLI_LINKSTACK_FREE(data->ids_todo); + } } -static void library_foreach_ID_link(Main *bmain, +/** \return false in case iteration over ID pointers must be stopped, true otherwise. */ +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -210,6 +229,10 @@ static void library_foreach_ID_link(Main *bmain, flag |= IDWALK_READONLY; flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS; + /* NOTE: This function itself should never be called recursively when IDWALK_RECURSE is set, + * see also comments in #BKE_library_foreach_ID_embedded. + * This is why we can always create this data here, and do not need to try and re-use it from + * `inherit_data`. */ data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); BLI_LINKSTACK_INIT(data.ids_todo); @@ -224,10 +247,26 @@ static void library_foreach_ID_link(Main *bmain, data.user_data = user_data; #define CALLBACK_INVOKE_ID(check_id, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS_ID(&data, check_id, cb_flag) + { \ + CHECK_TYPE_ANY((check_id), ID *, void *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) #define CALLBACK_INVOKE(check_id_super, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS_IDSUPER(&data, check_id_super, cb_flag) + { \ + CHECK_TYPE(&((check_id_super)->id), ID *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id_super), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; @@ -269,6 +308,10 @@ static void library_foreach_ID_link(Main *bmain, to_id_entry = to_id_entry->next) { BKE_lib_query_foreachid_process( &data, to_id_entry->id_pointer.to, to_id_entry->usage_flag); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } continue; } @@ -292,26 +335,33 @@ static void library_foreach_ID_link(Main *bmain, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } AnimData *adt = BKE_animdata_from_id(id); if (adt) { BKE_animdata_foreach_id(adt, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->foreach_id != NULL) { id_type->foreach_id(id, &data); - if (data.status & IDWALK_STOP) { - break; + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; } } } - if (data.ids_handled) { - BLI_gset_free(data.ids_handled, NULL); - BLI_LINKSTACK_FREE(data.ids_todo); - } + library_foreach_ID_data_cleanup(&data); + return true; #undef CALLBACK_INVOKE_ID #undef CALLBACK_INVOKE diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 905ac5af512..014c923f04f 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -132,7 +132,8 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) const bool is_obj = (GS(id_owner->name) == ID_OB); const bool is_obj_proxy = (is_obj && (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group)); - const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner)); + const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) && + (id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; @@ -669,57 +670,10 @@ void BKE_libblock_relink_ex( DEG_relations_tag_update(bmain); } +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag); static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; - if (cb_flag & IDWALK_CB_EMBEDDED) { - return IDWALK_RET_NOP; - } - - ID **id_pointer = cb_data->id_pointer; - ID *id = *id_pointer; - if (id) { - /* See: NEW_ID macro */ - if (id->newid) { - BKE_library_update_ID_link_user(id->newid, id, cb_flag); - id = id->newid; - *id_pointer = id; - } - if (id->tag & LIB_TAG_NEW) { - id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid(id); - } - } - return IDWALK_RET_NOP; -} - -/** - * Similar to #libblock_relink_ex, - * but is remapping IDs to their newid value if non-NULL, in given \a id. - * - * Very specific usage, not sure we'll keep it on the long run, - * currently only used in Object/Collection duplication code... - * - * WARNING: This is a deprecated version of this function, should not be used by new code. See - * #BKE_libblock_relink_to_newid_new below. - */ -void BKE_libblock_relink_to_newid(ID *id) -{ - if (ID_IS_LINKED(id)) { - return; - } - - BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); -} - -/* ************************ - * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this - * #BKE_libblock_relink_to_newid_new new code and remove old one. - ************************** */ -static void libblock_relink_to_newid_new(Main *bmain, ID *id); -static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) -{ - const int cb_flag = cb_data->cb_flag; if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_NOP; } @@ -729,31 +683,31 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) ID **id_pointer = cb_data->id_pointer; ID *id = *id_pointer; if (id) { + const int remap_flag = POINTER_AS_INT(cb_data->user_data); /* See: NEW_ID macro */ if (id->newid != NULL) { - BKE_libblock_relink_ex(bmain, - id_owner, - id, - id->newid, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + const int remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | + ID_REMAP_SKIP_OVERRIDE_LIBRARY; + BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, (short)remap_flag_final); id = id->newid; } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id, remap_flag); } } return IDWALK_RET_NOP; } -static void libblock_relink_to_newid_new(Main *bmain, ID *id) +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) { if (ID_IS_LINKED(id)) { return; } id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); + BKE_library_foreach_ID_link( + bmain, id, id_relink_to_newid_looper, POINTER_FROM_INT(remap_flag), 0); } /** @@ -765,7 +719,7 @@ static void libblock_relink_to_newid_new(Main *bmain, ID *id) * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... */ -void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) +void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) { if (ID_IS_LINKED(id)) { return; @@ -774,7 +728,7 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) BLI_assert(bmain->relations == NULL); BKE_layer_collection_resync_forbid(); - libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id, remap_flag); BKE_layer_collection_resync_allow(); BKE_main_collection_sync_remap(bmain); } diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index a6150028f46..05e8d4fe978 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -129,7 +129,8 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data) Light *lamp = (Light *)id; if (lamp->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree)); } } diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index a1c93920731..3c305d1fb3f 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -155,12 +155,14 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < MAX_MTEX; i++) { if (linestyle->mtex[i]) { - BKE_texture_mtex_foreach_id(data, linestyle->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_texture_mtex_foreach_id(data, linestyle->mtex[i])); } } if (linestyle->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree)); } LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index d82559a27cb..3a4e39812ab 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -166,9 +166,8 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) { Material *material = (Material *)id; /* Nodetrees **are owned by IDs**, treat them as mere sub-data and not real ID! */ - if (!BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)) { - return; - } + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)); if (material->texpaintslot != NULL) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP); } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index ef84afd8668..124db07298d 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -491,11 +491,11 @@ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, strip->act, IDWALK_CB_USER); LISTBASE_FOREACH (FCurve *, fcu, &strip->fcurves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) { - BKE_nla_strip_foreach_id(substrip, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_nla_strip_foreach_id(substrip, data)); } } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 6b409ae656e..688005454c8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -307,8 +307,10 @@ static void ntree_free_data(ID *id) static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) { - IDP_foreach_property( - sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { @@ -360,21 +362,25 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER); - IDP_foreach_property( - node->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(node->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index dc6ef580408..b5d39921f26 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -389,7 +389,8 @@ static void library_foreach_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, @@ -398,7 +399,8 @@ static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_shaderfxForeachIDLink(void *user_data, @@ -407,7 +409,8 @@ static void library_foreach_shaderfxForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), @@ -417,7 +420,8 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), @@ -426,7 +430,8 @@ static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(p int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void object_foreach_id(ID *id, LibraryForeachIDData *data) @@ -493,10 +498,18 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( data, proxy_cb_flag, false); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - IDP_foreach_property( - pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(pchan->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop( + &pchan->constraints, library_foreach_constraintObjectLooper, data)); } BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } @@ -508,14 +521,21 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); - BKE_gpencil_modifiers_foreach_ID_link( - object, library_foreach_gpencil_modifiersForeachIDLink, data); - BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data); - BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_gpencil_modifiers_foreach_ID_link( + object, library_foreach_gpencil_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data)); LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { - BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data)); } if (object->soft) { @@ -2854,7 +2874,7 @@ Object *BKE_object_duplicate(Main *bmain, if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&obn->id); + BKE_libblock_relink_to_newid(bmain, &obn->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index feb997a4c5d..5b62761bd91 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -180,7 +180,8 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < MAX_MTEX; i++) { if (psett->mtex[i]) { - BKE_texture_mtex_foreach_id(data, psett->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, psett->mtex[i])); } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index d7fc2005ab2..04106e6f42a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -471,7 +471,8 @@ static void scene_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSE int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } /** @@ -522,7 +523,10 @@ static void scene_foreach_toolsettings_id_pointer_process( } } -#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS( \ +/* Special handling is needed here, as `scene_foreach_toolsettings` (and its dependency + * `scene_foreach_paint`) are also used by `scene_undo_preserve`, where `LibraryForeachIDData + * *data` is NULL. */ +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER( \ __data, __id, __do_undo_restore, __action, __reader, __id_old, __cb_flag) \ { \ if (__do_undo_restore) { \ @@ -530,24 +534,38 @@ static void scene_foreach_toolsettings_id_pointer_process( (ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \ } \ else { \ + BLI_assert((__data) != NULL); \ BKE_LIB_FOREACHID_PROCESS_IDSUPER(__data, __id, __cb_flag); \ } \ } \ (void)0 +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( \ + __data, __do_undo_restore, __func_call) \ + { \ + if (__do_undo_restore) { \ + __func_call; \ + } \ + else { \ + BLI_assert((__data) != NULL); \ + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(__data, __func_call); \ + } \ + } \ + (void)0 + static void scene_foreach_paint(LibraryForeachIDData *data, Paint *paint, const bool do_undo_restore, BlendLibReader *reader, Paint *paint_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->brush, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->brush, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); for (int i = 0; i < paint_old->tool_slots_len; i++) { /* This is a bit tricky. * - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so @@ -559,21 +577,21 @@ static void scene_foreach_paint(LibraryForeachIDData *data, */ Brush *brush_tmp = NULL; Brush **brush_p = i < paint->tool_slots_len ? &paint->tool_slots[i].brush : &brush_tmp; - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - *brush_p, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); - } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->palette, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->palette, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + *brush_p, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); + } + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->palette, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->palette, + IDWALK_CB_USER); } static void scene_foreach_toolsettings(LibraryForeachIDData *data, @@ -582,110 +600,152 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, BlendLibReader *reader, ToolSettings *toolsett_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.scene, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.scene, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.object, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.shape_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.shape_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.scene, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.scene, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.object, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.shape_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.shape_object, + IDWALK_CB_NOP); scene_foreach_paint( data, &toolsett->imapaint.paint, do_undo_restore, reader, &toolsett_old->imapaint.paint); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.stencil, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.stencil, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.clone, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.clone, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.canvas, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.canvas, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.stencil, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.stencil, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.clone, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.clone, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.canvas, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.canvas, + IDWALK_CB_USER); if (toolsett->vpaint) { - scene_foreach_paint( - data, &toolsett->vpaint->paint, do_undo_restore, reader, &toolsett_old->vpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->vpaint->paint, + do_undo_restore, + reader, + &toolsett_old->vpaint->paint)); } if (toolsett->wpaint) { - scene_foreach_paint( - data, &toolsett->wpaint->paint, do_undo_restore, reader, &toolsett_old->wpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->wpaint->paint, + do_undo_restore, + reader, + &toolsett_old->wpaint->paint)); } if (toolsett->sculpt) { - scene_foreach_paint( - data, &toolsett->sculpt->paint, do_undo_restore, reader, &toolsett_old->sculpt->paint); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->sculpt->gravity_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->sculpt->gravity_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->sculpt->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->sculpt->gravity_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->sculpt->gravity_object, + IDWALK_CB_NOP); } if (toolsett->uvsculpt) { - scene_foreach_paint( - data, &toolsett->uvsculpt->paint, do_undo_restore, reader, &toolsett_old->uvsculpt->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->uvsculpt->paint, + do_undo_restore, + reader, + &toolsett_old->uvsculpt->paint)); } if (toolsett->gp_paint) { - scene_foreach_paint( - data, &toolsett->gp_paint->paint, do_undo_restore, reader, &toolsett_old->gp_paint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_paint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_paint->paint)); } if (toolsett->gp_vertexpaint) { - scene_foreach_paint(data, - &toolsett->gp_vertexpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_vertexpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_vertexpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_vertexpaint->paint)); } if (toolsett->gp_sculptpaint) { - scene_foreach_paint(data, - &toolsett->gp_sculptpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_sculptpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_sculptpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_sculptpaint->paint)); } if (toolsett->gp_weightpaint) { - scene_foreach_paint(data, - &toolsett->gp_weightpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_weightpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_weightpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_weightpaint->paint)); } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->gp_sculpt.guide.reference_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->gp_sculpt.guide.reference_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->gp_sculpt.guide.reference_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->gp_sculpt.guide.reference_object, + IDWALK_CB_NOP); } +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL + static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb) { LISTBASE_FOREACH (LayerCollection *, lc, lb) { @@ -707,7 +767,8 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) #define FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return false; \ } \ } \ @@ -746,15 +807,18 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->r.bake.cage_object, IDWALK_CB_NOP); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree)); } if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data)); } /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (scene->master_collection != NULL) { - BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection)); } LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { @@ -765,7 +829,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE); } - scene_foreach_layer_collection(data, &view_layer->layer_collections); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_layer_collection(data, &view_layer->layer_collections)); LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { if (fmc->script) { @@ -786,18 +851,25 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); - IDP_foreach_property( - marker->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(marker->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } ToolSettings *toolsett = scene->toolsettings; if (toolsett) { - scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett)); } if (scene->rigidbody_world) { - BKE_rigidbody_world_id_loop( - scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_rigidbody_world_id_loop( + scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data)); } } @@ -1853,7 +1925,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&sce_copy->id); + BKE_libblock_relink_to_newid(bmain, &sce_copy->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 233a4f344b5..6352e08ec4b 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -97,6 +97,10 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet * } } +/** + * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of + * `IDTypeInfo` structure). + */ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP); @@ -107,10 +111,8 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area switch (sl->spacetype) { case SPACE_VIEW3D: { View3D *v3d = (View3D *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP); - if (v3d->localvd) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } @@ -118,13 +120,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_GRAPH: { SpaceGraph *sipo = (SpaceGraph *)sl; - - screen_foreach_id_dopesheet(data, sipo->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, sipo->ads)); break; } case SPACE_PROPERTIES: { SpaceProperties *sbuts = (SpaceProperties *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, sbuts->pinid, IDWALK_CB_NOP); break; } @@ -132,14 +133,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area break; case SPACE_ACTION: { SpaceAction *saction = (SpaceAction *)sl; - screen_foreach_id_dopesheet(data, &saction->ads); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP); break; } case SPACE_IMAGE: { SpaceImage *sima = (SpaceImage *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER); @@ -147,33 +146,28 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER); break; } case SPACE_NLA: { SpaceNla *snla = (SpaceNla *)sl; - - screen_foreach_id_dopesheet(data, snla->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, snla->ads)); break; } case SPACE_TEXT: { SpaceText *st = (SpaceText *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP); break; } case SPACE_SCRIPT: { SpaceScript *scpt = (SpaceScript *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP); break; } case SPACE_OUTLINER: { SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, space_outliner->search_tse.id, IDWALK_CB_NOP); - if (space_outliner->treestore != NULL) { TreeStoreElem *tselem; BLI_mempool_iter iter; @@ -187,13 +181,11 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - const bool is_private_nodetree = snode->id != NULL && ntreeFromID(snode->id) == snode->nodetree; BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); @@ -219,14 +211,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE); BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); break; } case SPACE_SPREADSHEET: { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { if (context->type == SPREADSHEET_CONTEXT_OBJECT) { BKE_LIB_FOREACHID_PROCESS_IDSUPER( @@ -243,12 +233,13 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area static void screen_foreach_id(ID *id, LibraryForeachIDData *data) { - if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { - bScreen *screen = (bScreen *)id; + if ((BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) == 0) { + return; + } + bScreen *screen = (bScreen *)id; - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - BKE_screen_foreach_id_screen_area(data, area); - } + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_screen_foreach_id_screen_area(data, area)); } } diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 1d297b3ced9..98e7405bde6 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -103,7 +103,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) Simulation *simulation = (Simulation *)id; if (simulation->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree)); } } diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index c878abf0dff..a8c8eaa6a1c 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -142,7 +142,8 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) Tex *texture = (Tex *)id; if (texture->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree)); } BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER); } diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index fe03c5b817a..2f0a282a298 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -131,7 +131,8 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data) if (world->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree)); } } diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh index 7b8aa03b807..051731ab801 100644 --- a/source/blender/blenlib/BLI_serialize.hh +++ b/source/blender/blenlib/BLI_serialize.hh @@ -103,11 +103,11 @@ using IntValue = PrimitiveValue<int64_t, eValueType::Int>; using DoubleValue = PrimitiveValue<double, eValueType::Double>; using BooleanValue = PrimitiveValue<bool, eValueType::Boolean>; -template<typename Container, typename ContainerItem, eValueType V> class ContainerValue; +template<typename Container, eValueType V, typename ContainerItem = typename Container::value_type> +class ContainerValue; /* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can * be created by calling `create_lookup`. */ -using ArrayValue = - ContainerValue<Vector<std::shared_ptr<Value>>, std::shared_ptr<Value>, eValueType::Array>; +using ArrayValue = ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Array>; /** * Class containing a (de)serializable value. @@ -138,7 +138,7 @@ class Value { public: virtual ~Value() = default; - const eValueType type() const + eValueType type() const { return type_; } @@ -234,11 +234,11 @@ template< /** The container type where the elements are stored in. */ typename Container, - /** Type of the data inside the container. */ - typename ContainerItem, - /** ValueType representing the value (object/array). */ - eValueType V> + eValueType V, + + /** Type of the data inside the container. */ + typename ContainerItem> class ContainerValue : public Value { public: using Items = Container; @@ -275,8 +275,7 @@ using ObjectElementType = std::pair<std::string, std::shared_ptr<Value>>; * Object is a key-value container where the key must be a std::string. * Internally it is stored in a blender::Vector to ensure the order of keys. */ -class ObjectValue - : public ContainerValue<Vector<ObjectElementType>, ObjectElementType, eValueType::Object> { +class ObjectValue : public ContainerValue<Vector<ObjectElementType>, eValueType::Object> { public: using LookupValue = std::shared_ptr<Value>; using Lookup = Map<std::string, LookupValue>; diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index 8afa631ffc5..f06f6f7d329 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -65,7 +65,6 @@ void BlendfileLoadingBaseTest::SetUpTestCase() BKE_idtype_init(); BKE_appdir_init(); IMB_init(); - BKE_images_init(); BKE_modifier_init(); DEG_register_node_types(); RNA_init(); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 0baf994d978..3577b836d77 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -112,8 +112,8 @@ set(SRC intern/draw_view_data.cc intern/smaa_textures.c engines/basic/basic_engine.c - engines/image/image_engine.c - engines/image/image_shader.c + engines/image/image_engine.cc + engines/image/image_shader.cc engines/eevee/eevee_bloom.c engines/eevee/eevee_cryptomatte.c engines/eevee/eevee_data.c @@ -220,7 +220,7 @@ set(SRC engines/eevee/eevee_private.h engines/external/external_engine.h engines/image/image_engine.h - engines/image/image_private.h + engines/image/image_private.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h engines/select/select_engine.h diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.cc index 8b4acfbd5e3..776639f5224 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.cc @@ -39,7 +39,9 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" + +namespace blender::draw::image_engine { #define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0) #define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) @@ -107,7 +109,7 @@ static void space_image_gpu_texture_get(Image *image, { const DRWContextState *draw_ctx = DRW_context_state_get(); SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - if (image->rr != NULL) { + if (image->rr != nullptr) { /* Update multi-index and pass for the current eye. */ BKE_image_multilayer_index(image->rr, &sima->iuser); } @@ -115,14 +117,14 @@ static void space_image_gpu_texture_get(Image *image, BKE_image_multiview_index(image, &sima->iuser); } - if (ibuf == NULL) { + if (ibuf == nullptr) { return; } - if (ibuf->rect == NULL && ibuf->rect_float == NULL) { + if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { /* This code-path is only supposed to happen when drawing a lazily-allocatable render result. - * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return NULL as - * an image buffer when it has no pixels. */ + * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return nullptr + * as an image buffer when it has no pixels. */ BLI_assert(image->type == IMA_TYPE_R_RESULT); @@ -150,7 +152,7 @@ static void space_image_gpu_texture_get(Image *image, } else if (image->source == IMA_SRC_TILED) { *r_gpu_texture = BKE_image_get_gpu_tiles(image, iuser, ibuf); - *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, NULL); + *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, nullptr); *r_owns_texture = false; } else { @@ -168,7 +170,7 @@ static void space_node_gpu_texture_get(Image *image, { *r_gpu_texture = BKE_image_get_gpu_texture(image, iuser, ibuf); *r_owns_texture = false; - *r_tex_tile_data = NULL; + *r_tex_tile_data = nullptr; } static void image_gpu_texture_get(Image *image, @@ -204,7 +206,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser const char space_type = draw_ctx->space_data->spacetype; const Scene *scene = draw_ctx->scene; - GPUTexture *tex_tile_data = NULL; + GPUTexture *tex_tile_data = nullptr; image_gpu_texture_get(image, iuser, ibuf, &pd->texture, &pd->owns_texture, &tex_tile_data); if (pd->texture) { @@ -218,7 +220,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser } const bool use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, ibuf); - const bool is_tiled_texture = tex_tile_data != NULL; + const bool is_tiled_texture = tex_tile_data != nullptr; int draw_flags = 0; if (space_type == SPACE_IMAGE) { @@ -307,11 +309,11 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture); DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass); if (is_tiled_texture) { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT); DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data); } else { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT); } DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", far_near); DRW_shgroup_uniform_vec4_copy(shgrp, "color", color); @@ -332,13 +334,13 @@ static void IMAGE_engine_init(void *ved) IMAGE_Data *vedata = (IMAGE_Data *)ved; IMAGE_StorageList *stl = vedata->stl; if (!stl->pd) { - stl->pd = MEM_callocN(sizeof(IMAGE_PrivateData), __func__); + stl->pd = static_cast<IMAGE_PrivateData *>(MEM_callocN(sizeof(IMAGE_PrivateData), __func__)); } IMAGE_PrivateData *pd = stl->pd; - pd->ibuf = NULL; - pd->lock = NULL; - pd->texture = NULL; + pd->ibuf = nullptr; + pd->lock = nullptr; + pd->texture = nullptr; } static void IMAGE_cache_init(void *ved) @@ -352,14 +354,14 @@ static void IMAGE_cache_init(void *ved) { /* Write depth is needed for background overlay rendering. Near depth is used for * transparency checker and Far depth is used for indicating the image size. */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | - DRW_STATE_BLEND_ALPHA_PREMUL; + DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); psl->image_pass = DRW_pass_create("Image", state); } const SpaceLink *space_link = draw_ctx->space_data; const char space_type = space_link->spacetype; - pd->view = NULL; + pd->view = nullptr; if (space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; Image *image = ED_space_image(sima); @@ -372,15 +374,15 @@ static void IMAGE_cache_init(void *ved) ARegion *region = draw_ctx->region; Main *bmain = CTX_data_main(draw_ctx->evil_C); Image *image = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &pd->lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, nullptr, &pd->lock); { /* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */ float winmat[4][4], viewmat[4][4]; orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0); unit_m4(winmat); - pd->view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); + pd->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr); } - image_cache_image(vedata, image, NULL, ibuf); + image_cache_image(vedata, image, nullptr, ibuf); pd->image = image; pd->ibuf = ibuf; } @@ -405,14 +407,14 @@ static void image_draw_finish(IMAGE_Data *ved) else if (space_type == SPACE_NODE) { BKE_image_release_ibuf(pd->image, pd->ibuf, pd->lock); } - pd->image = NULL; - pd->ibuf = NULL; + pd->image = nullptr; + pd->ibuf = nullptr; if (pd->texture && pd->owns_texture) { GPU_texture_free(pd->texture); pd->owns_texture = false; } - pd->texture = NULL; + pd->texture = nullptr; } static void IMAGE_draw_scene(void *ved) @@ -428,11 +430,11 @@ static void IMAGE_draw_scene(void *ved) DRW_view_set_active(pd->view); DRW_draw_pass(psl->image_pass); - DRW_view_set_active(NULL); + DRW_view_set_active(nullptr); image_draw_finish(vedata); } -static void IMAGE_engine_free(void) +static void IMAGE_engine_free() { IMAGE_shader_free(); } @@ -441,19 +443,27 @@ static void IMAGE_engine_free(void) static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data); +} // namespace blender::draw::image_engine + +extern "C" { + +using namespace blender::draw::image_engine; + DrawEngineType draw_engine_image_type = { - NULL, /* next */ - NULL, /* prev */ + nullptr, /* next */ + nullptr, /* prev */ N_("UV/Image"), /* idname */ &IMAGE_data_size, /* vedata_size */ &IMAGE_engine_init, /* engine_init */ &IMAGE_engine_free, /* engine_free */ &IMAGE_cache_init, /* cache_init */ &IMAGE_cache_populate, /* cache_populate */ - NULL, /* cache_finish */ + nullptr, /* cache_finish */ &IMAGE_draw_scene, /* draw_scene */ - NULL, /* view_update */ - NULL, /* id_update */ - NULL, /* render_to_image */ - NULL, /* store_metadata */ + nullptr, /* view_update */ + nullptr, /* id_update */ + nullptr, /* render_to_image */ + nullptr, /* store_metadata */ }; +} + diff --git a/source/blender/draw/engines/image/image_engine.h b/source/blender/draw/engines/image/image_engine.h index 0098d863ef9..ba1e87088f1 100644 --- a/source/blender/draw/engines/image/image_engine.h +++ b/source/blender/draw/engines/image/image_engine.h @@ -22,4 +22,13 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + extern DrawEngineType draw_engine_image_type; + +#ifdef __cplusplus +} +#endif + diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.hh index 76a94e68da1..434e27eff65 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.hh @@ -20,10 +20,6 @@ * \ingroup draw_engine */ -#ifdef __cplusplus -extern "C" { -#endif - /* Forward declarations */ struct GPUTexture; struct ImBuf; @@ -31,13 +27,15 @@ struct Image; /* *********** LISTS *********** */ +namespace blender::draw::image_engine { + /* GPUViewport.storage * Is freed every time the viewport engine changes. */ -typedef struct IMAGE_PassList { +struct IMAGE_PassList { DRWPass *image_pass; -} IMAGE_PassList; +}; -typedef struct IMAGE_PrivateData { +struct IMAGE_PrivateData { void *lock; struct ImBuf *ibuf; struct Image *image; @@ -45,25 +43,24 @@ typedef struct IMAGE_PrivateData { struct GPUTexture *texture; bool owns_texture; -} IMAGE_PrivateData; +}; -typedef struct IMAGE_StorageList { +struct IMAGE_StorageList { IMAGE_PrivateData *pd; -} IMAGE_StorageList; +}; -typedef struct IMAGE_Data { +struct IMAGE_Data { void *engine_type; DRWViewportEmptyList *fbl; DRWViewportEmptyList *txl; IMAGE_PassList *psl; IMAGE_StorageList *stl; -} IMAGE_Data; +}; /* image_shader.c */ GPUShader *IMAGE_shader_image_get(bool is_tiled_image); void IMAGE_shader_library_ensure(void); void IMAGE_shader_free(void); -#ifdef __cplusplus -} -#endif +} // namespace blender::draw::image_engine + diff --git a/source/blender/draw/engines/image/image_shader.c b/source/blender/draw/engines/image/image_shader.cc index 691c0d7029a..1c6abf36505 100644 --- a/source/blender/draw/engines/image/image_shader.c +++ b/source/blender/draw/engines/image/image_shader.cc @@ -27,27 +27,31 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" +extern "C" { extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_globals_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; extern char datatoc_engine_image_frag_glsl[]; extern char datatoc_engine_image_vert_glsl[]; +} + +namespace blender::draw::image_engine { -typedef struct IMAGE_Shaders { +struct IMAGE_Shaders { GPUShader *image_sh[2]; -} IMAGE_Shaders; +}; static struct { IMAGE_Shaders shaders; DRWShaderLibrary *lib; -} e_data = {{{0}}}; /* Engine data */ +} e_data = {{{nullptr}}}; /* Engine data */ -void IMAGE_shader_library_ensure(void) +void IMAGE_shader_library_ensure() { - if (e_data.lib == NULL) { + if (e_data.lib == nullptr) { e_data.lib = DRW_shader_library_create(); /* NOTE: These need to be ordered by dependencies. */ DRW_SHADER_LIB_ADD(e_data.lib, common_colormanagement_lib); @@ -60,18 +64,18 @@ GPUShader *IMAGE_shader_image_get(bool is_tiled_image) { const int index = is_tiled_image ? 1 : 0; IMAGE_Shaders *sh_data = &e_data.shaders; - if (!sh_data->image_sh[index]) { + if (sh_data->image_sh[index] == nullptr) { sh_data->image_sh[index] = DRW_shader_create_with_shaderlib( datatoc_engine_image_vert_glsl, - NULL, + nullptr, datatoc_engine_image_frag_glsl, e_data.lib, - is_tiled_image ? "#define TILED_IMAGE\n" : NULL); + is_tiled_image ? "#define TILED_IMAGE\n" : nullptr); } return sh_data->image_sh[index]; } -void IMAGE_shader_free(void) +void IMAGE_shader_free() { GPUShader **sh_data_as_array = (GPUShader **)&e_data.shaders; for (int i = 0; i < (sizeof(IMAGE_Shaders) / sizeof(GPUShader *)); i++) { @@ -80,3 +84,5 @@ void IMAGE_shader_free(void) DRW_SHADER_LIB_FREE_SAFE(e_data.lib); } + +} // namespace blender::draw::image_engine diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index a9810b4cc77..1abd056502d 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -15,13 +15,15 @@ #include "engines/eevee/eevee_private.h" #include "engines/gpencil/gpencil_engine.h" -#include "engines/image/image_private.h" +#include "engines/image/image_private.hh" #include "engines/overlay/overlay_private.h" #include "engines/workbench/workbench_private.h" #include "intern/draw_shader.h" namespace blender::draw { +using namespace blender::draw::image_engine; + static void test_workbench_glsl_shaders() { workbench_shader_library_ensure(); diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 6fe32699907..05837ed17b9 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -145,6 +145,22 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) } } } + /* For node sockets, it is useful to include the node name as well (multiple similar nodes + * are not distinguishable otherwise). Unfortunately, the node label cannot be retrieved + * from the rna path, for this to work access to the underlying node is needed (but finding + * the node iterates all nodes & sockets which would result in bad performance in some + * circumstances). */ + if (RNA_struct_is_a(ptr.type, &RNA_NodeSocket)) { + char nodename[256]; + if (BLI_str_quoted_substr(fcu->rna_path, "nodes[", nodename, sizeof(nodename))) { + const char *structname_all = BLI_sprintfN("%s : %s", nodename, structname); + if (free_structname) { + MEM_freeN((void *)structname); + } + structname = structname_all; + free_structname = 1; + } + } } /* Property Name is straightforward */ diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 8e1e5be2e47..9634665be7b 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -19,7 +19,6 @@ */ #include "BKE_asset_catalog.hh" -#include "BKE_asset_catalog_path.hh" #include "BKE_asset_library.hh" #include "BKE_main.h" diff --git a/source/blender/editors/asset/intern/asset_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc index 329342a30cd..c22bbc923eb 100644 --- a/source/blender/editors/asset/intern/asset_filter.cc +++ b/source/blender/editors/asset/intern/asset_filter.cc @@ -22,7 +22,6 @@ #include "BLI_listbase.h" -#include "DNA_ID.h" #include "DNA_asset_types.h" #include "ED_asset_filter.h" diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 5c8d0b1349c..363bd9226da 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -26,7 +26,6 @@ #include <string> -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BLO_readfile.h" diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index c57d121a18f..1a2d3f5837a 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -27,7 +27,6 @@ #include "BKE_preferences.h" -#include "DNA_asset_types.h" #include "DNA_userdef_types.h" #include "UI_resources.h" diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 4bc15e842fc..b5ea054fb5d 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -32,7 +32,6 @@ #include "BLI_path_util.h" #include "BLI_utility_mixins.hh" -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_preferences.h" @@ -40,7 +39,6 @@ #include "ED_fileselect.h" #include "WM_api.h" -#include "WM_types.h" /* XXX uses private header of file-space. */ #include "../space_file/filelist.h" diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index a0a2c63b407..2e5bdb63359 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -20,9 +20,6 @@ * Functions for marking and clearing assets. */ -#include <memory> -#include <string> - #include "DNA_ID.h" #include "BKE_asset.h" @@ -32,8 +29,6 @@ #include "BKE_lib_id.h" #include "BKE_main.h" -#include "BLO_readfile.h" - #include "UI_interface_icons.h" #include "RNA_access.h" diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index d2fd8ab88a4..f7c567c89f6 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -18,19 +18,13 @@ * \ingroup edasset */ -#include "BKE_asset.h" -#include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_report.h" -#include "BLI_string_ref.hh" -#include "BLI_vector.hh" - #include "ED_asset.h" -#include "ED_asset_catalog.hh" /* XXX needs access to the file list, should all be done via the asset system in future. */ #include "ED_fileselect.h" @@ -38,7 +32,6 @@ #include "RNA_define.h" #include "WM_api.h" -#include "WM_types.h" using namespace blender; diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index f664eab5cbb..f136c08f129 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -23,7 +23,6 @@ #include <new> -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_report.h" diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 1b44cf88db1..6f18798bd2a 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -2155,8 +2155,8 @@ void FONT_OT_open(wmOperatorType *ot) FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, - FILE_DEFAULTDISPLAY, - FILE_SORT_DEFAULT); + FILE_IMGDISPLAY, + FILE_SORT_ALPHA); } /** \} */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index ba603cdd6ec..bdb4e485373 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -123,6 +123,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -972,12 +974,13 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p) depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + const ViewDepths *depths = p->depths; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1086,7 +1089,10 @@ static bool annotation_stroke_eraser_is_occluded(tGPsdata *p, const int mval_i[2] = {x, y}; float mval_3d[3]; - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, &pt->x); @@ -1211,7 +1217,8 @@ static void annotation_stroke_doeraser(tGPsdata *p) if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + ED_view3d_depth_override( + p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } } @@ -1499,6 +1506,9 @@ static void annotation_session_cleanup(tGPsdata *p) static void annotation_session_free(tGPsdata *p) { + if (p->depths) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 1b69947b294..9860c75f290 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -127,6 +127,8 @@ typedef struct tGPDfill { struct bGPDstroke *gps_mouse; /** Pointer to report messages. */ struct ReportList *reports; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** flags */ short flag; /** avoid too fast events */ @@ -1374,7 +1376,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpf->win, tgpf->region); ED_view3d_depth_override( - tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, &tgpf->depths); /* Since strokes are so fine, when using their depth we need a margin * otherwise they might get missed. */ @@ -1385,6 +1387,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int interp_depth = 0; int found_depth = 0; + const ViewDepths *depths = tgpf->depths; tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { @@ -1392,11 +1395,9 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int mval_i[2]; round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpf->region, mval_i, depth_margin, tgpf->depth_arr + i) == - 0) && - (i && - (ED_view3d_autodist_depth_seg( - tgpf->region, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1771,6 +1772,11 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d); } + /* Remove depth buffer in cache. */ + if (tgpf->depths) { + ED_view3d_depths_free(tgpf->depths); + } + /* finally, free memory used by temp data */ MEM_freeN(tgpf); } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index b6730cb123b..3f3fd4fff39 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -155,6 +155,8 @@ typedef struct tGPDprimitive { struct Material *material; /** current brush */ struct Brush *brush; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** Settings to pass to gp_points_to_xy(). */ GP_SpaceConversion gsc; diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 3e9f22f25d3..8d72ea72532 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -161,6 +161,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -1090,14 +1092,16 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) int found_depth = 0; depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + + const ViewDepths *depths = p->depths; int i; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1346,7 +1350,10 @@ static bool gpencil_stroke_eraser_is_occluded( /* calculate difference matrix if parent object */ BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat); - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); mul_v3_m4v3(fpt, diff_mat, &pt->x); @@ -1733,7 +1740,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p) if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } /* loop over all layers too, since while it's easy to restrict editing to @@ -2087,6 +2094,9 @@ static void gpencil_session_free(tGPsdata *p) if (p->rng != NULL) { BLI_rng_free(p->rng); } + if (p->depths != NULL) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } @@ -2267,8 +2277,9 @@ static void gpencil_paint_initstroke(tGPsdata *p, static void gpencil_paint_strokeend(tGPsdata *p) { ToolSettings *ts = p->scene->toolsettings; - /* for surface sketching, need to set the right OpenGL context stuff so that - * the conversions will project the values correctly... + const bool is_eraser = (p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) != 0; + /* for surface sketching, need to set the right OpenGL context stuff so + * that the conversions will project the values correctly... */ if (gpencil_project_check(p)) { View3D *v3d = p->area->spacedata.first; @@ -2282,11 +2293,11 @@ static void gpencil_paint_strokeend(tGPsdata *p) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - NULL); + is_eraser ? NULL : &p->depths); } /* check if doing eraser or not */ - if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { + if (!is_eraser) { /* transfer stroke to frame */ gpencil_stroke_newfrombuffer(p); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index f8cfc130e35..7382aca9a87 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -795,15 +795,16 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - NULL); + &tgpi->depths); depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); + const ViewDepths *depths = tgpi->depths; tGPspoint *ptc = &points2D[0]; for (int i = 0; i < gps->totpoints; i++, ptc++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpi->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - tgpi->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1154,6 +1155,11 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op) BLI_rng_free(tgpi->rng); } + /* Remove depth buffer in cache. */ + if (tgpi->depths) { + ED_view3d_depths_free(tgpi->depths); + } + MEM_freeN(tgpi); } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index d3640c6eebd..c4319fd7ee2 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -979,7 +979,7 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc, * to 3D coordinates. * * \param point2D: The screen-space 2D point data to convert. - * \param depth: Depth array (via #ED_view3d_autodist_depth()). + * \param depth: Depth array (via #ED_view3d_depth_read_cached()). * \param r_out: The resulting 2D point data. */ void gpencil_stroke_convertcoords_tpoint(Scene *scene, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index ebf3316a509..f9ccd916327 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -609,12 +609,8 @@ bool ED_view3d_autodist_simple(struct ARegion *region, float mouse_worldloc[3], int margin, const float *force_depth); -bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth); -bool ED_view3d_autodist_depth_seg(struct ARegion *region, - const int mval_sta[2], - const int mval_end[2], - int margin, - float *depth); +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth); /* select */ #define MAXPICKELEMS 2500 diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 90caeecd91f..8b5894923ad 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2145,7 +2145,7 @@ static void copy_object_set_idnew(bContext *C) Main *bmain = CTX_data_main(C); CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - BKE_libblock_relink_to_newid(&ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id, 0); } CTX_DATA_END; @@ -2378,7 +2378,7 @@ static void make_object_duplilist_real(bContext *C, Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); /* Remap new object to itself, and clear again newid pointer of orig object. */ - BKE_libblock_relink_to_newid(&ob_dst->id); + BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0); DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); @@ -3374,8 +3374,11 @@ Base *ED_object_add_duplicate( ob = basen->object; - /* link own references to the newly duplicated data T26816. */ - BKE_libblock_relink_to_newid(&ob->id); + /* Link own references to the newly duplicated data T26816. + * Note that this function can be called from edit-mode code, in which case we may have to + * enforce remapping obdata (by default this is forbidden in edit mode). */ + const int remap_flag = BKE_object_is_in_editmode(ob) ? ID_REMAP_FORCE_OBDATA_IN_EDITMODE : 0; + BKE_libblock_relink_to_newid(bmain, &ob->id, remap_flag); /* DAG_relations_tag_update(bmain); */ /* caller must do */ diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index aa15ce36582..b51644eebf3 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1685,18 +1685,20 @@ static bool single_data_needs_duplication(ID *id) return (id != NULL && (id->us > 1 || ID_IS_LINKED(id))); } -static void libblock_relink_collection(Collection *collection, const bool do_collection) +static void libblock_relink_collection(Main *bmain, + Collection *collection, + const bool do_collection) { if (do_collection) { - BKE_libblock_relink_to_newid(&collection->id); + BKE_libblock_relink_to_newid(bmain, &collection->id, 0); } for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) { - BKE_libblock_relink_to_newid(&cob->ob->id); + BKE_libblock_relink_to_newid(bmain, &cob->ob->id, 0); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - libblock_relink_collection(child->collection, true); + libblock_relink_collection(bmain, child->collection, true); } } @@ -1766,10 +1768,10 @@ static void single_object_users( single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); /* Will also handle the master collection. */ - BKE_libblock_relink_to_newid(&scene->id); + BKE_libblock_relink_to_newid(bmain, &scene->id, 0); /* Collection and object pointers in collections */ - libblock_relink_collection(scene->master_collection, false); + libblock_relink_collection(bmain, scene->master_collection, false); /* We also have to handle runtime things in UI. */ if (v3d) { @@ -2654,7 +2656,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) /* api callbacks */ ot->invoke = drop_named_material_invoke; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index a1b1c8cc363..a73fa2b9740 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1911,6 +1911,7 @@ static void filelist_clear_asset_library(FileList *filelist) { /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ filelist->asset_library = NULL; + file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); } void filelist_clear_ex(struct FileList *filelist, @@ -2010,7 +2011,6 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } - file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index f04336cab84..22e03cfa46c 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -48,37 +48,10 @@ #include "graph_intern.h" -/* ******************** GRAPH SLIDER OPERATORS ************************* */ -/* This file contains a collection of operators to modify keyframes in the graph editor. All - * operators are modal and use a slider that allows the user to define a percentage to modify the - * operator. */ - -/* ******************** Decimate Keyframes Operator ************************* */ - -static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max) -{ - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* Loop through filtered data and clean curves. */ - for (ale = anim_data.first; ale; ale = ale->next) { - if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) { - /* The selection contains unsupported keyframe types! */ - WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!"); - } - - ale->update |= ANIM_UPDATE_DEFAULT; - } - - ANIM_animdata_update(ac, &anim_data); - ANIM_animdata_freelist(&anim_data); -} +/* Used to obtain a list of animation channels for the operators to work on. */ +#define OPERATOR_DATA_FILTER \ + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \ + ANIMFILTER_NODUPLIS) /* ------------------- */ @@ -110,20 +83,88 @@ typedef enum tDecimModes { DECIM_ERROR, } tDecimModes; +/* ******************** GRAPH SLIDER OPERATORS ************************* */ +/* This file contains a collection of operators to modify keyframes in the graph editor. All + * operators are modal and use a slider that allows the user to define a percentage to modify the + * operator. */ + +/* ******************** Utility Functions ************************* */ + +/* Construct a list with the original bezt arrays so we can restore them during modal operation. + * The data is stored on the struct that is passed.*/ +static void store_original_bezt_arrays(tDecimateGraphOp *dgo) +{ + ListBase anim_data = {NULL, NULL}; + bAnimContext *ac = &dgo->ac; + bAnimListElem *ale; + + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + /* Loop through filtered data and copy the curves. */ + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + + if (fcu->bezt == NULL) { + /* This curve is baked, skip it. */ + continue; + } + + const int arr_size = sizeof(BezTriple) * fcu->totvert; + + tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); + BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); + + copy->tot_vert = fcu->totvert; + memcpy(bezts_copy, fcu->bezt, arr_size); + + copy->bezt = bezts_copy; + + LinkData *link = NULL; + + link = MEM_callocN(sizeof(LinkData), "Bezt Link"); + link->data = copy; + + BLI_addtail(&dgo->bezt_arr_list, link); + } + + ANIM_animdata_freelist(&anim_data); +} + +/* ******************** Decimate Keyframes Operator ************************* */ + +static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + + /* Filter data. */ + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + /* Loop through filtered data and clean curves. */ + for (ale = anim_data.first; ale; ale = ale->next) { + if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) { + /* The selection contains unsupported keyframe types! */ + WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!"); + } + + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + /* Overwrite the current bezts arrays with the original data. */ static void decimate_reset_bezts(tDecimateGraphOp *dgo) { ListBase anim_data = {NULL, NULL}; LinkData *link_bezt; bAnimListElem *ale; - int filter; bAnimContext *ac = &dgo->ac; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); /* Loop through filtered data and reset bezts. */ for (ale = anim_data.first, link_bezt = dgo->bezt_arr_list.first; ale; ale = ale->next) { @@ -229,56 +270,14 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); + store_original_bezt_arrays(dgo); + dgo->slider = ED_slider_create(C); ED_slider_init(dgo->slider, event); ED_slider_allow_overshoot_set(dgo->slider, false); decimate_draw_status(C, dgo); - /* Construct a list with the original bezt arrays so we can restore them during modal operation. - */ - { - ListBase anim_data = {NULL, NULL}; - bAnimContext *ac = &dgo->ac; - bAnimListElem *ale; - - int filter; - - /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* Loop through filtered data and copy the curves. */ - for (ale = anim_data.first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - - if (fcu->bezt == NULL) { - /* This curve is baked, skip it. */ - continue; - } - - const int arr_size = sizeof(BezTriple) * fcu->totvert; - - tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); - BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); - - copy->tot_vert = fcu->totvert; - memcpy(bezts_copy, fcu->bezt, arr_size); - - copy->bezt = bezts_copy; - - LinkData *link = NULL; - - link = MEM_callocN(sizeof(LinkData), "Bezt Link"); - link->data = copy; - - BLI_addtail(&dgo->bezt_arr_list, link); - } - - ANIM_animdata_freelist(&anim_data); - } - if (dgo->bezt_arr_list.first == NULL) { WM_report(RPT_WARNING, "Fcurve Decimate: Can't decimate baked channels. Unbake them and try again."); diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index 91fe1bc01b7..192b80881ee 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -67,4 +67,14 @@ set(SRC set(LIB ) +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + blender_add_lib(bf_editor_space_spreadsheet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index d54af7ffe2c..50b67c55bd6 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -319,6 +319,8 @@ static float get_default_column_width(const ColumnValues &values) return 4.0f * float_width; case SPREADSHEET_VALUE_TYPE_INSTANCES: return 8.0f; + case SPREADSHEET_VALUE_TYPE_STRING: + return 5.0f; } return float_width; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 97170693cb3..c11b4a2b23d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -58,6 +58,7 @@ class CellValue { std::optional<ObjectCellValue> value_object; std::optional<CollectionCellValue> value_collection; std::optional<GeometrySetCellValue> value_geometry_set; + std::optional<std::string> value_string; }; } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index c1d345d1861..cf048d1bbd8 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -20,6 +20,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_volume.h" #include "DNA_ID.h" #include "DNA_mesh_types.h" @@ -33,6 +34,11 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + #include "FN_field_cpp_type.hh" #include "bmesh.h" @@ -112,6 +118,9 @@ std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( r_cell_value.value_color = *( const ColorGeometry4f *)value; break; + case SPREADSHEET_VALUE_TYPE_STRING: + r_cell_value.value_string = *(const std::string *)value; + break; case SPREADSHEET_VALUE_TYPE_INSTANCES: break; } @@ -487,6 +496,91 @@ int InstancesDataSource::tot_rows() const return component_->instances_amount(); } +void VolumeDataSource::foreach_default_column_ids( + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const +{ + if (component_->is_empty()) { + return; + } + + for (const char *name : {"Grid Name", "Data Type", "Class"}) { + SpreadsheetColumnID column_id{(char *)name}; + fn(column_id, false); + } +} + +std::unique_ptr<ColumnValues> VolumeDataSource::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return {}; + } + +#ifdef WITH_OPENVDB + const int size = this->tot_rows(); + if (STREQ(column_id.name, "Grid Name")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Grid Name"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + r_cell_value.value_string = BKE_volume_grid_name(volume_grid); + }, + 6.0f); + } + if (STREQ(column_id.name, "Data Type")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Type"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + const VolumeGridType type = BKE_volume_grid_type(volume_grid); + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_volume_grid_data_type_items, type, &name); + r_cell_value.value_string = IFACE_(name); + }, + 5.0f); + } + if (STREQ(column_id.name, "Class")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Class"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + openvdb::GridClass grid_class = grid->getGridClass(); + if (grid_class == openvdb::GridClass::GRID_FOG_VOLUME) { + r_cell_value.value_string = IFACE_("Fog Volume"); + } + else if (grid_class == openvdb::GridClass::GRID_LEVEL_SET) { + r_cell_value.value_string = IFACE_("Level Set"); + } + else { + r_cell_value.value_string = IFACE_("Unkown"); + } + }, + 5.0f); + } +#else + UNUSED_VARS(column_id); +#endif + + return {}; +} + +int VolumeDataSource::tot_rows() const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return 0; + } + return BKE_volume_num_grids(volume); +} + GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, Object *object_eval, const GeometryComponentType used_component_type) @@ -682,6 +776,9 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { return std::make_unique<InstancesDataSource>(geometry_set, std::move(extra_columns)); } + if (component_type == GEO_COMPONENT_TYPE_VOLUME) { + return std::make_unique<VolumeDataSource>(geometry_set); + } return std::make_unique<GeometryDataSource>( object_eval, geometry_set, component_type, domain, std::move(extra_columns)); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index 6c88a94f585..a4114dd1f6a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -116,6 +116,26 @@ class InstancesDataSource : public DataSource { int tot_rows() const override; }; +class VolumeDataSource : public DataSource { + const GeometrySet geometry_set_; + const VolumeComponent *component_; + + public: + VolumeDataSource(GeometrySet geometry_set) + : geometry_set_(std::move(geometry_set)), + component_(geometry_set_.get_component_for_read<VolumeComponent>()) + { + } + + void foreach_default_column_ids( + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override; + + std::unique_ptr<ColumnValues> get_column_values( + const SpreadsheetColumnID &column_id) const override; + + int tot_rows() const override; +}; + std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval); } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index 4cf6d14cbda..b9a508d7764 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -20,6 +20,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_context.h" +#include "BKE_volume.h" #include "BLF_api.h" @@ -48,7 +49,7 @@ static int is_component_row_selected(struct uiBut *but, const void *arg) const bool is_domain_selected = (AttributeDomain)sspreadsheet->attribute_domain == domain; bool is_selected = is_component_selected && is_domain_selected; - if (component == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { is_selected = is_component_selected; } @@ -141,6 +142,14 @@ static int element_count_from_instances(const GeometrySet &geometry_set) return 0; } +static int element_count_from_volume(const GeometrySet &geometry_set) +{ + if (const Volume *volume = geometry_set.get_volume_for_read()) { + return BKE_volume_num_grids(volume); + } + return 0; +} + static int element_count_from_component_domain(const GeometrySet &geometry_set, GeometryComponentType component, AttributeDomain domain) @@ -191,6 +200,10 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, BLI_str_format_attribute_domain_size( element_count, element_count_from_instances(draw_context.current_geometry_set)); } + if (component == GEO_COMPONENT_TYPE_VOLUME) { + BLI_str_format_attribute_domain_size( + element_count, element_count_from_volume(draw_context.current_geometry_set)); + } else { BLI_str_format_attribute_domain_size( element_count, @@ -237,7 +250,7 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, void DatasetRegionDrawer::draw_component_row(const DatasetComponentLayoutInfo &component_info) { - if (component_info.type == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component_info.type, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { draw_dataset_row( 0, component_info.type, std::nullopt, component_info.icon, component_info.label, true); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc index abbad8c7088..f15af2e4d32 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc @@ -75,6 +75,12 @@ constexpr DatasetComponentLayoutInfo DATASET_layout_hierarchy[] = { }, }, { + GEO_COMPONENT_TYPE_VOLUME, + N_("Volume Grids"), + ICON_VOLUME_DATA, + {}, + }, + { GEO_COMPONENT_TYPE_INSTANCES, N_("Instances"), ICON_EMPTY_AXIS, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 355899be279..202523c0e64 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -228,6 +228,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { 0, nullptr); } + else if (cell_value.value_string.has_value()) { + uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + cell_value.value_string->c_str(), + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } } void draw_float_vector(const CellDrawParams ¶ms, const Span<float> values) const diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index d56049990b4..a07abac4474 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -105,12 +105,15 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter, return row_filter.value_string; } return ""; - case SPREADSHEET_VALUE_TYPE_COLOR: + case SPREADSHEET_VALUE_TYPE_COLOR: { std::ostringstream result; result.precision(3); result << std::fixed << "(" << row_filter.value_color[0] << ", " << row_filter.value_color[1] << ", " << row_filter.value_color[2] << ", " << row_filter.value_color[3] << ")"; return result.str(); + } + case SPREADSHEET_VALUE_TYPE_STRING: + return row_filter.value_string; } BLI_assert_unreachable(); return ""; @@ -234,6 +237,8 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel) uiItemR(layout, filter_ptr, "value_color", 0, IFACE_("Value"), ICON_NONE); uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE); break; + case SPREADSHEET_VALUE_TYPE_STRING: + break; } } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 6acf51aec6e..3a9c9e27ef0 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -853,7 +853,6 @@ static void view3d_dropboxes(void) drop->draw = WM_drag_draw_item_name_fn; drop->draw_activate = view3d_ob_drop_draw_activate; drop->draw_deactivate = view3d_ob_drop_draw_deactivate; - drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */ drop = WM_dropbox_add(lb, "OBJECT_OT_transform_to_mouse", @@ -865,7 +864,6 @@ static void view3d_dropboxes(void) drop->draw = WM_drag_draw_item_name_fn; drop->draw_activate = view3d_ob_drop_draw_activate; drop->draw_deactivate = view3d_ob_drop_draw_deactivate; - drop->opcontext = WM_OP_INVOKE_DEFAULT; WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e09453b9957..dab1b55072a 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1094,17 +1094,10 @@ bool ED_view3d_autodist_simple(ARegion *region, return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } -bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) -{ - *depth = view_autodist_depth_margin(region, mval, margin); - - return (*depth != FLT_MAX); -} - static bool depth_segment_cb(int x, int y, void *userData) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } *data = userData; @@ -1114,27 +1107,25 @@ static bool depth_segment_cb(int x, int y, void *userData) mval[0] = x; mval[1] = y; - depth = view_autodist_depth_margin(data->region, mval, data->margin); - - if (depth != FLT_MAX) { + if (ED_view3d_depth_read_cached(data->vd, mval, data->margin, &depth)) { data->depth = depth; return false; } return true; } -bool ED_view3d_autodist_depth_seg( - ARegion *region, const int mval_sta[2], const int mval_end[2], int margin, float *depth) +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } data = {NULL}; int p1[2]; int p2[2]; - data.region = region; + data.vd = vd; data.margin = margin; data.depth = FLT_MAX; @@ -1691,6 +1682,8 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, return true; } + /* GPencil and Anotations also need the returned depth value to be high so that it is invalid. */ + *r_depth = FLT_MAX; return false; } diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 2464eb05a6d..64c8fd3e3a9 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -142,6 +142,12 @@ typedef enum eImageTextureResolution { IMA_TEXTURE_RESOLUTION_LEN } eImageTextureResolution; +typedef struct Image_Runtime { + /* Mutex used to guarantee thread-safe access to the cached ImBuf of the corresponding image ID. + */ + void *cache_mutex; +} Image_Runtime; + typedef struct Image { ID id; @@ -208,6 +214,8 @@ typedef struct Image { /** ImageView. */ ListBase views; struct Stereo3dFormat *stereo3d_format; + + Image_Runtime runtime; } Image; /* **************** IMAGE ********************* */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 5ebc81fff4f..671cc182d7e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -2006,6 +2006,7 @@ typedef enum eSpreadsheetColumnValueType { SPREADSHEET_VALUE_TYPE_FLOAT3 = 4, SPREADSHEET_VALUE_TYPE_COLOR = 5, SPREADSHEET_VALUE_TYPE_INSTANCES = 6, + SPREADSHEET_VALUE_TYPE_STRING = 7, } eSpreadsheetColumnValueType; /** diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index f3e15d08fa3..fb18802483d 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -211,6 +211,8 @@ DEF_ENUM(rna_enum_attribute_domain_items) DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) +DEF_ENUM(rna_enum_volume_grid_data_type_items) + DEF_ENUM(rna_enum_collection_color_items) DEF_ENUM(rna_enum_strip_color_items) diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index f6b8b55688c..3100c1195f4 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -33,6 +33,26 @@ #include "BLI_math_base.h" +const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { + {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, + {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, + {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, + {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, + {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, + {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, + {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, + {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, + {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, + {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, + {VOLUME_GRID_POINTS, + "POINTS", + 0, + "Points (Unsupported)", + "Points grid, currently unsupported by volume objects"}, + {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DEG_depsgraph.h" @@ -244,30 +264,10 @@ static void rna_def_volume_grid(BlenderRNA *brna) prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL); RNA_def_property_ui_text(prop, "Name", "Volume grid name"); - static const EnumPropertyItem data_type_items[] = { - {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, - {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, - {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, - {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, - {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, - {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, - {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, - {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, - {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, - {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, - {VOLUME_GRID_POINTS, - "POINTS", - 0, - "Points (Unsupported)", - "Points grid, currently unsupported by volume objects"}, - {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, - {0, NULL, 0, NULL, NULL}, - }; - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_funcs(prop, "rna_VolumeGrid_data_type_get", NULL, NULL); - RNA_def_property_enum_items(prop, data_type_items); + RNA_def_property_enum_items(prop, rna_enum_volume_grid_data_type_items); RNA_def_property_ui_text(prop, "Data Type", "Data type of voxel values"); prop = RNA_def_property(srna, "channels", PROP_INT, PROP_UNSIGNED); diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 167765fa131..ac346eb705b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -67,13 +67,20 @@ Mesh *create_grid_mesh(const int verts_x, const float size_x, const float size_y); +struct ConeAttributeOutputs { + StrongAnonymousAttributeID top_id; + StrongAnonymousAttributeID bottom_id; + StrongAnonymousAttributeID side_id; +}; + Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, const int circle_segments, const int side_segments, const int fill_segments, - const GeometryNodeMeshCircleFillType fill_type); + const GeometryNodeMeshCircleFillType fill_type, + ConeAttributeOutputs &attribute_outputs); Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc index 4c89aba2e6d..ee7a78ccf71 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -24,7 +24,16 @@ namespace blender::nodes { static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>(N_("Factor")).field_source(); + b.add_output<decl::Float>(N_("Factor")) + .field_source() + .description( + N_("For points, the portion of the spline's total length at the control point. For " + "Splines, the factor of that spline within the entire curve")); + b.add_output<decl::Float>(N_("Length")) + .field_source() + .description( + N_("For points, the distance along the control point's spline, For splines, the " + "distance along the entire curve")); } /** @@ -32,47 +41,42 @@ static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for * each spline is the portion of the total length at the start of the spline. */ -static Array<float> curve_parameter_spline_domain(const CurveEval &curve, const IndexMask mask) +static Array<float> curve_length_spline_domain(const CurveEval &curve, + const IndexMask UNUSED(mask)) { Span<SplinePtr> splines = curve.splines(); float length = 0.0f; - Array<float> parameters(splines.size()); + Array<float> lengths(splines.size()); for (const int i : splines.index_range()) { - parameters[i] = length; + lengths[i] = length; length += splines[i]->length(); } - const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; - mask.foreach_index([&](const int64_t i) { parameters[i] *= total_length_inverse; }); - - return parameters; + return lengths; } /** * The parameter at each control point is the factor at the corresponding evaluated point. */ -static void calculate_bezier_parameters(const BezierSpline &spline, MutableSpan<float> parameters) +static void calculate_bezier_lengths(const BezierSpline &spline, MutableSpan<float> lengths) { Span<int> offsets = spline.control_point_offsets(); - Span<float> lengths = spline.evaluated_lengths(); - const float total_length = spline.length(); - const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; - + Span<float> lengths_eval = spline.evaluated_lengths(); for (const int i : IndexRange(1, spline.size() - 1)) { - parameters[i] = lengths[offsets[i] - 1] * total_length_inverse; + lengths[i] = lengths_eval[offsets[i] - 1]; } } /** * The parameter for poly splines is simply the evaluated lengths divided by the total length. */ -static void calculate_poly_parameters(const PolySpline &spline, MutableSpan<float> parameters) +static void calculate_poly_length(const PolySpline &spline, MutableSpan<float> lengths) { - Span<float> lengths = spline.evaluated_lengths(); - const float total_length = spline.length(); - const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; - - for (const int i : IndexRange(1, spline.size() - 1)) { - parameters[i] = lengths[i - 1] * total_length_inverse; + Span<float> lengths_eval = spline.evaluated_lengths(); + if (spline.is_cyclic()) { + lengths.drop_front(1).copy_from(lengths_eval.drop_back(1)); + } + else { + lengths.drop_front(1).copy_from(lengths_eval); } } @@ -82,52 +86,47 @@ static void calculate_poly_parameters(const PolySpline &spline, MutableSpan<floa * each point is not well defined. So instead, treat the control points as if they were a poly * spline. */ -static void calculate_nurbs_parameters(const NURBSpline &spline, MutableSpan<float> parameters) +static void calculate_nurbs_lengths(const NURBSpline &spline, MutableSpan<float> lengths) { Span<float3> positions = spline.positions(); Array<float> control_point_lengths(spline.size()); - float length = 0.0f; for (const int i : IndexRange(positions.size() - 1)) { - parameters[i] = length; + lengths[i] = length; length += float3::distance(positions[i], positions[i + 1]); } - - const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; - for (float ¶meter : parameters) { - parameter *= total_length_inverse; - } + lengths.last() = length; } -static Array<float> curve_parameter_point_domain(const CurveEval &curve) +static Array<float> curve_length_point_domain(const CurveEval &curve) { Span<SplinePtr> splines = curve.splines(); Array<int> offsets = curve.control_point_offsets(); const int total_size = offsets.last(); - Array<float> parameters(total_size); + Array<float> lengths(total_size); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { const Spline &spline = *splines[i]; - MutableSpan spline_factors{parameters.as_mutable_span().slice(offsets[i], spline.size())}; + MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; spline_factors.first() = 0.0f; switch (splines[i]->type()) { case Spline::Type::Bezier: { - calculate_bezier_parameters(static_cast<const BezierSpline &>(spline), spline_factors); + calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); break; } case Spline::Type::Poly: { - calculate_poly_parameters(static_cast<const PolySpline &>(spline), spline_factors); + calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); break; } case Spline::Type::NURBS: { - calculate_nurbs_parameters(static_cast<const NURBSpline &>(spline), spline_factors); + calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); break; } } } }); - return parameters; + return lengths; } static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, @@ -136,13 +135,50 @@ static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, ResourceScope &scope) { if (domain == ATTR_DOMAIN_POINT) { - Array<float> parameters = curve_parameter_point_domain(curve); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters)); + Span<SplinePtr> splines = curve.splines(); + Array<float> values = curve_length_point_domain(curve); + + const Array<int> offsets = curve.control_point_offsets(); + for (const int i_spline : curve.splines().index_range()) { + const Spline &spline = *splines[i_spline]; + const float spline_length = spline.length(); + const float spline_length_inv = spline_length == 0.0f ? 0.0f : 1.0f / spline_length; + for (const int i : IndexRange(spline.size())) { + values[offsets[i_spline] + i] *= spline_length_inv; + } + } + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(values)); } if (domain == ATTR_DOMAIN_CURVE) { - Array<float> parameters = curve_parameter_spline_domain(curve, mask); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters)); + Array<float> values = curve.accumulated_spline_lengths(); + const float total_length_inv = values.last() == 0.0f ? 0.0f : 1.0f / values.last(); + for (const int i : mask) { + values[i] *= total_length_inv; + } + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(values)); + } + return nullptr; +} + +static const GVArray *construct_curve_length_gvarray(const CurveEval &curve, + const IndexMask mask, + const AttributeDomain domain, + ResourceScope &scope) +{ + if (domain == ATTR_DOMAIN_POINT) { + Array<float> lengths = curve_length_point_domain(curve); + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(lengths)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + if (curve.splines().size() == 1) { + Array<float> lengths(1, 0.0f); + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(lengths)); + } + + Array<float> lengths = curve_length_spline_domain(curve, mask); + return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(lengths)); } return nullptr; @@ -188,10 +224,51 @@ class CurveParameterFieldInput final : public fn::FieldInput { } }; +class CurveLengthFieldInput final : public fn::FieldInput { + public: + CurveLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Length node") + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_curve_length_gvarray(*curve, mask, domain, scope); + } + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 345634563454; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; + } +}; + static void geo_node_curve_parameter_exec(GeoNodeExecParams params) { Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; + Field<float> length_field{std::make_shared<CurveLengthFieldInput>()}; params.set_output("Factor", std::move(parameter_field)); + params.set_output("Length", std::move(length_field)); } } // namespace blender::nodes @@ -199,7 +276,6 @@ static void geo_node_curve_parameter_exec(GeoNodeExecParams params) void register_node_type_geo_curve_parameter() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec; ntype.declare = blender::nodes::geo_node_curve_parameter_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index a755d47cc6a..673a5095044 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -29,15 +29,27 @@ static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuild .default_value(16) .min(1) .max(256) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .description(N_("The number of evaluated points on the curve")); b.add_input<decl::Vector>(N_("Start")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the start control point of the curve")); b.add_input<decl::Vector>(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>(N_("End Handle")).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the start handle used to define the shape of the curve. In Offset mode, " + "relative to Start point")); + b.add_input<decl::Vector>(N_("End Handle")) + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the end handle used to define the shape of the curve. In Offset mode, " + "relative to End point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the end control point of the curve")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index bf4f22d6578..ffede480c75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -25,17 +25,34 @@ namespace blender::nodes { static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Resolution")).default_value(32).min(3).max(512); + b.add_input<decl::Int>(N_("Resolution")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle")); b.add_input<decl::Vector>(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); b.add_input<decl::Vector>(N_("Point 2")) .default_value({0.0f, 1.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); b.add_input<decl::Vector>(N_("Point 3")) .default_value({1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the points from the origin")); b.add_output<decl::Geometry>(N_("Curve")); b.add_output<decl::Vector>(N_("Center")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 5b215797052..37a5989d3f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -25,10 +25,21 @@ namespace blender::nodes { static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>(N_("Start")).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>(N_("End")).default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>(N_("Direction")).default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>(N_("Length")).default_value(1.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Vector>(N_("Start")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the second control point")); + b.add_input<decl::Vector>(N_("Direction")) + .default_value({0.0f, 0.0f, 1.0f}) + .description( + N_("Direction the line is going in. The length of this vector does not matter")); + b.add_input<decl::Float>(N_("Length")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance between the two points")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 6041ddee02d..27bf4a310df 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -25,14 +25,20 @@ static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBui .default_value(16) .min(3) .max(256) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .description(N_("The number of edges on the curve")); b.add_input<decl::Vector>(N_("Start")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); b.add_input<decl::Vector>(N_("Middle")) .default_value({0.0f, 2.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>(N_("End")).default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_("Position of the middle control point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the last control point")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 7260da05a8d..e00a502bf32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -23,31 +23,57 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Height")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Height")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The Y axis size of the shape")); b.add_input<decl::Float>(N_("Bottom Width")) .default_value(4.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Top Width")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Offset")).default_value(1.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Top Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Offset")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("For Parallelogram, the relative X difference between the top and bottom edges. For " + "Trapezoid, the amount to move the top edge in the positive X axis")); b.add_input<decl::Float>(N_("Bottom Height")) .default_value(3.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Top Height")).default_value(1.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The distance between the bottom point and the X axis")); + b.add_input<decl::Float>(N_("Top Height")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the top point and the X axis")); b.add_input<decl::Vector>(N_("Point 1")) .default_value({-1.0f, -1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input<decl::Vector>(N_("Point 2")) .default_value({1.0f, -1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input<decl::Vector>(N_("Point 3")) .default_value({1.0f, 1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_input<decl::Vector>(N_("Point 4")) .default_value({-1.0f, 1.0f, 0.0f}) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 1dc9cd7f107..2a872fd82cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -26,12 +26,28 @@ static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) .default_value(32) .min(1) .max(1024) - .subtype(PROP_UNSIGNED); - b.add_input<decl::Float>(N_("Rotations")).default_value(2.0f).min(0.0f); - b.add_input<decl::Float>(N_("Start Radius")).default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("End Radius")).default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Height")).default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Bool>(N_("Reverse")); + .subtype(PROP_UNSIGNED) + .description(N_("Number of points in one rotation of the spiral")); + b.add_input<decl::Float>(N_("Rotations")) + .default_value(2.0f) + .min(0.0f) + .description(N_("Number of times the spiral makes a full rotation")); + b.add_input<decl::Float>(N_("Start Radius")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the start of the spiral")); + b.add_input<decl::Float>(N_("End Radius")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the end of the spiral")); + b.add_input<decl::Float>(N_("Height")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description(N_("The height perpendicular to the base of the spiral")); + b.add_input<decl::Bool>(N_("Reverse")) + .description(N_("Switch the direction from clockwise to counterclockwise")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index b5bafce17c6..7f682b198b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -22,16 +22,25 @@ namespace blender::nodes { static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Points")).default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input<decl::Int>(N_("Points")) + .default_value(8) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points on each of the circles")); b.add_input<decl::Float>(N_("Inner Radius")) .default_value(1.0f) .min(0.0f) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the inner circle; can be larger than outer radius")); b.add_input<decl::Float>(N_("Outer Radius")) .default_value(2.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Twist")).subtype(PROP_ANGLE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the outer circle; can be smaller than inner radius")); + b.add_input<decl::Float>(N_("Twist")) + .subtype(PROP_ANGLE) + .description(N_("The counterclockwise rotation of the inner set of points")); b.add_output<decl::Geometry>(N_("Curve")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 945dac5650b..fb2021c307b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -41,6 +41,7 @@ static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) .min(0.001f) .supports_field() .subtype(PROP_DISTANCE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field(); b.add_output<decl::Geometry>(N_("Curve")); } @@ -74,6 +75,7 @@ struct SampleModeParam { GeometryNodeCurveResampleMode mode; std::optional<Field<float>> length; std::optional<Field<int>> count; + Field<bool> selection; }; static SplinePtr resample_spline(const Spline &src, const int count) @@ -183,42 +185,64 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.count); + evaluator.add(mode_param.selection); evaluator.evaluate(); const VArray<int> &cuts = evaluator.get_evaluated<int>(0); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); - output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + if (selections[i]) { + output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.add(*mode_param.length); + evaluator.add(mode_param.selection); evaluator.evaluate(); const VArray<float> &lengths = evaluator.get_evaluated<float>(0); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float divide_length = std::max(lengths[i], 0.0001f); - const float spline_length = input_splines[i]->length(); - const int count = std::max(int(spline_length / divide_length) + 1, 1); - output_splines[i] = resample_spline(*input_splines[i], count); + if (selections[i]) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float divide_length = std::max(lengths[i], 0.0001f); + const float spline_length = input_splines[i]->length(); + const int count = std::max(int(spline_length / divide_length) + 1, 1); + output_splines[i] = resample_spline(*input_splines[i], count); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - output_splines[i] = resample_spline_evaluated(*input_splines[i]); + if (selections[i]) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - output_curve->attributes = input_curve->attributes; - return output_curve; } @@ -244,6 +268,8 @@ static void geo_node_resample_exec(GeoNodeExecParams params) SampleModeParam mode_param; mode_param.mode = mode; + mode_param.selection = params.extract_input<Field<bool>>("Selection"); + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { Field<int> count = params.extract_input<Field<int>>("Count"); if (count < 1) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 685a8faff5c..f1f95be107a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -29,8 +29,15 @@ namespace blender::nodes { static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Vertices")).default_value(32).min(3); - b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>(N_("Vertices")) + .default_value(32) + .min(3) + .description(N_("Number of vertices on the circle")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the vertices from the origin")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 206d48d40c8..01378431ca6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -25,20 +25,45 @@ #include "node_geometry_util.hh" +#include <cmath> + namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Vertices")).default_value(32).min(3).max(512); - b.add_input<decl::Int>(N_("Side Segments")).default_value(1).min(1).max(512); - b.add_input<decl::Int>(N_("Fill Segments")).default_value(1).min(1).max(512); - b.add_input<decl::Float>(N_("Radius Top")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>(N_("Vertices")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle at the top and bottom")); + b.add_input<decl::Int>(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of edges running vertically along the side of the cone")); + b.add_input<decl::Int>(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("Number of concentric rings used to fill the round face")); + b.add_input<decl::Float>(N_("Radius Top")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the top circle of the cone")); b.add_input<decl::Float>(N_("Radius Bottom")) .default_value(1.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Depth")).default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .description(N_("Radius of the bottom circle of the cone")); + b.add_input<decl::Float>(N_("Depth")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Height of the generated cone")); b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Bool>(N_("Top")).field_source(); + b.add_output<decl::Bool>(N_("Bottom")).field_source(); + b.add_output<decl::Bool>(N_("Side")).field_source(); } static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -94,6 +119,8 @@ struct ConeConfig { int tot_edge_rings; int tot_verts; int tot_edges; + int tot_corners; + int tot_faces; /* Helpful vertex indices. */ int first_vert; @@ -107,6 +134,14 @@ struct ConeConfig { int last_fan_edges_start; int last_edge; + /* Helpful face indices. */ + int top_faces_start; + int top_faces_len; + int side_faces_start; + int side_faces_len; + int bottom_faces_start; + int bottom_faces_len; + ConeConfig(float radius_top, float radius_bottom, float depth, @@ -133,6 +168,7 @@ struct ConeConfig { this->tot_edge_rings = this->calculate_total_edge_rings(); this->tot_verts = this->calculate_total_verts(); this->tot_edges = this->calculate_total_edges(); + this->tot_corners = this->calculate_total_corners(); this->first_vert = 0; this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert; @@ -144,6 +180,36 @@ struct ConeConfig { this->tot_quad_rings * this->circle_segments * 2; this->last_fan_edges_start = this->tot_edges - this->circle_segments; this->last_edge = this->tot_edges - 1; + + this->top_faces_start = 0; + if (!this->top_is_point) { + this->top_faces_len = (fill_segments - 1) * circle_segments; + this->top_faces_len += this->top_has_center_vert ? circle_segments : 0; + this->top_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0; + } + else { + this->top_faces_len = 0; + } + + this->side_faces_start = this->top_faces_len; + if (this->top_is_point && this->bottom_is_point) { + this->side_faces_len = 0; + } + else { + this->side_faces_len = side_segments * circle_segments; + } + + if (!this->bottom_is_point) { + this->bottom_faces_len = (fill_segments - 1) * circle_segments; + this->bottom_faces_len += this->bottom_has_center_vert ? circle_segments : 0; + this->bottom_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0; + } + else { + this->bottom_faces_len = 0; + } + this->bottom_faces_start = this->side_faces_start + this->side_faces_len; + + this->tot_faces = this->top_faces_len + this->side_faces_len + this->bottom_faces_len; } private: @@ -151,10 +217,7 @@ struct ConeConfig { int calculate_total_edge_rings(); int calculate_total_verts(); int calculate_total_edges(); - - public: - int get_tot_corners() const; - int get_tot_faces() const; + int calculate_total_corners(); }; int ConeConfig::calculate_total_quad_rings() @@ -248,7 +311,7 @@ int ConeConfig::calculate_total_edges() return edge_total; } -int ConeConfig::get_tot_corners() const +int ConeConfig::calculate_total_corners() { if (top_is_point && bottom_is_point) { return 0; @@ -275,32 +338,6 @@ int ConeConfig::get_tot_corners() const return corner_total; } -int ConeConfig::get_tot_faces() const -{ - if (top_is_point && bottom_is_point) { - return 0; - } - - int face_total = 0; - if (top_has_center_vert) { - face_total += circle_segments; - } - else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - - face_total += tot_quad_rings * circle_segments; - - if (bottom_has_center_vert) { - face_total += circle_segments; - } - else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - - return face_total; -} - static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config) { Array<float2> circle(config.circle_segments); @@ -522,6 +559,60 @@ static void calculate_cone_faces(const MutableSpan<MLoop> &loops, } } +static void calculate_selection_outputs(Mesh *mesh, + const ConeConfig &config, + ConeAttributeOutputs &attribute_outputs) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + + /* Populate "Top" selection output. */ + if (attribute_outputs.top_id) { + const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); + MutableSpan<bool> selection = attribute.as_span(); + + if (config.top_is_point) { + selection[config.first_vert] = true; + } + else { + selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true); + } + attribute.save(); + } + + /* Populate "Bottom" selection output. */ + if (attribute_outputs.bottom_id) { + const bool face = !config.bottom_is_point && + config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); + MutableSpan<bool> selection = attribute.as_span(); + + if (config.bottom_is_point) { + selection[config.last_vert] = true; + } + else { + selection + .slice(config.bottom_faces_start, + face ? config.bottom_faces_len : config.circle_segments) + .fill(true); + } + attribute.save(); + } + + /* Populate "Side" selection output. */ + if (attribute_outputs.side_id) { + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE); + MutableSpan<bool> selection = attribute.as_span(); + + selection.slice(config.side_faces_start, config.side_faces_len).fill(true); + attribute.save(); + } +} + /** * If the top is the cone tip or has a fill, it is unwrapped into a circle in the * lower left quadrant of the UV. @@ -665,7 +756,8 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, const int circle_segments, const int side_segments, const int fill_segments, - const GeometryNodeMeshCircleFillType fill_type) + const GeometryNodeMeshCircleFillType fill_type, + ConeAttributeOutputs &attribute_outputs) { const ConeConfig config( radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); @@ -683,7 +775,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, } Mesh *mesh = BKE_mesh_new_nomain( - config.tot_verts, config.tot_edges, 0, config.get_tot_corners(), config.get_tot_faces()); + config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces); BKE_id_material_eval_ensure_default_slot(&mesh->id); MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; @@ -695,6 +787,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, calculate_cone_edges(edges, config); calculate_cone_faces(loops, polys, config); calculate_cone_uvs(mesh, config); + calculate_selection_outputs(mesh, config, attribute_outputs); BKE_mesh_normals_tag_dirty(mesh); @@ -708,38 +801,76 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) storage.fill_type; + auto return_default = [&]() { + params.set_output("Top", fn::make_constant_field<bool>(false)); + params.set_output("Bottom", fn::make_constant_field<bool>(false)); + params.set_output("Side", fn::make_constant_field<bool>(false)); + params.set_output("Mesh", GeometrySet()); + }; + const int circle_segments = params.extract_input<int>("Vertices"); if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); } const int side_segments = params.extract_input<int>("Side Segments"); if (side_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); } const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments"); if (fill_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); } const float radius_top = params.extract_input<float>("Radius Top"); const float radius_bottom = params.extract_input<float>("Radius Bottom"); const float depth = params.extract_input<float>("Depth"); - Mesh *mesh = create_cylinder_or_cone_mesh( - radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); + ConeAttributeOutputs attribute_outputs; + if (params.output_is_required("Top")) { + attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); + } + if (params.output_is_required("Bottom")) { + attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); + } + if (params.output_is_required("Side")) { + attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); + } + + Mesh *mesh = create_cylinder_or_cone_mesh(radius_top, + radius_bottom, + depth, + circle_segments, + side_segments, + fill_segments, + fill_type, + attribute_outputs); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); + if (attribute_outputs.top_id) { + params.set_output("Top", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.top_id), params.attribute_producer_name())); + } + if (attribute_outputs.bottom_id) { + params.set_output( + "Bottom", + AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id), + params.attribute_producer_name())); + } + if (attribute_outputs.side_id) { + params.set_output("Side", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.side_id), params.attribute_producer_name())); + } + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index 3a211993bdc..b5903f7b71e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -29,10 +29,23 @@ static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) b.add_input<decl::Vector>(N_("Size")) .default_value(float3(1)) .min(0.0f) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Int>(N_("Vertices X")).default_value(2).min(2).max(1000); - b.add_input<decl::Int>(N_("Vertices Y")).default_value(2).min(2).max(1000); - b.add_input<decl::Int>(N_("Vertices Z")).default_value(2).min(2).max(1000); + .subtype(PROP_TRANSLATION) + .description(N_("Side length along each axis")); + b.add_input<decl::Int>(N_("Vertices X")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the X side of the shape")); + b.add_input<decl::Int>(N_("Vertices Y")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Y side of the shape")); + b.add_input<decl::Int>(N_("Vertices Z")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Z side of the shape")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 3bcf42b40b1..51ecff72e68 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -33,17 +33,17 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) .default_value(32) .min(3) .max(512) - .description(N_("The number of vertices around the circumference")); + .description(N_("The number of vertices on the top and bottom circles")); b.add_input<decl::Int>(N_("Side Segments")) .default_value(1) .min(1) .max(512) - .description(N_("The number of segments along the side")); + .description(N_("The number of rectangular segments along each side")); b.add_input<decl::Int>(N_("Fill Segments")) .default_value(1) .min(1) .max(512) - .description(N_("The number of concentric segments of the fill")); + .description(N_("The number of concentric rings used to fill the round faces")); b.add_input<decl::Float>(N_("Radius")) .default_value(1.0f) .min(0.0f) @@ -53,8 +53,11 @@ static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description(N_("The height of the cylinder on the Z axis")); + .description(N_("The height of the cylinder")); b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Bool>(N_("Top")).field_source(); + b.add_output<decl::Bool>(N_("Bottom")).field_source(); + b.add_output<decl::Bool>(N_("Side")).field_source(); } static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, @@ -97,33 +100,71 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) storage.fill_type; + auto return_default = [&]() { + params.set_output("Top", fn::make_constant_field<bool>(false)); + params.set_output("Bottom", fn::make_constant_field<bool>(false)); + params.set_output("Side", fn::make_constant_field<bool>(false)); + params.set_output("Mesh", GeometrySet()); + }; + const float radius = params.extract_input<float>("Radius"); const float depth = params.extract_input<float>("Depth"); const int circle_segments = params.extract_input<int>("Vertices"); if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); } const int side_segments = params.extract_input<int>("Side Segments"); if (side_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); } const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments"); if (fill_segments < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); - params.set_output("Mesh", GeometrySet()); - return; + return return_default(); + } + + ConeAttributeOutputs attribute_outputs; + if (params.output_is_required("Top")) { + attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); + } + if (params.output_is_required("Bottom")) { + attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); + } + if (params.output_is_required("Side")) { + attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); } /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ - Mesh *mesh = create_cylinder_or_cone_mesh( - radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type); + Mesh *mesh = create_cylinder_or_cone_mesh(radius, + radius, + depth, + circle_segments, + side_segments, + fill_segments, + fill_type, + attribute_outputs); + + if (attribute_outputs.top_id) { + params.set_output("Top", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.top_id), params.attribute_producer_name())); + } + if (attribute_outputs.bottom_id) { + params.set_output( + "Bottom", + AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id), + params.attribute_producer_name())); + } + if (attribute_outputs.side_id) { + params.set_output("Side", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.side_id), params.attribute_producer_name())); + } params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index c4e476981c1..73c679e18f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -29,10 +29,26 @@ namespace blender::nodes { static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Size X")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Size Y")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>(N_("Vertices X")).default_value(3).min(2).max(1000); - b.add_input<decl::Int>(N_("Vertices Y")).default_value(3).min(2).max(1000); + b.add_input<decl::Float>(N_("Size X")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the X direction")); + b.add_input<decl::Float>(N_("Size Y")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the Y direction")); + b.add_input<decl::Int>(N_("Vertices X")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the X direction")); + b.add_input<decl::Int>(N_("Vertices Y")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the Y direction")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index da3dfef3aea..f49cc5e7e18 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -28,8 +28,16 @@ namespace blender::nodes { static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>(N_("Subdivisions")).default_value(1).min(1).max(7); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_input<decl::Int>(N_("Subdivisions")) + .default_value(1) + .min(1) + .max(7) + .description(N_("Number of subdivisions on top of the basic icosahedron")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 6515afe5966..df4efb2427c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -29,12 +29,25 @@ namespace blender::nodes { static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(10000); - b.add_input<decl::Float>(N_("Resolution")).default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>(N_("Start Location")).subtype(PROP_TRANSLATION); + b.add_input<decl::Int>(N_("Count")) + .default_value(10) + .min(1) + .max(10000) + .description(N_("Number of vertices on the line")); + b.add_input<decl::Float>(N_("Resolution")) + .default_value(1.0f) + .min(0.1f) + .subtype(PROP_DISTANCE) + .description(N_("Length of each individual edge")); + b.add_input<decl::Vector>(N_("Start Location")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first vertex")); b.add_input<decl::Vector>(N_("Offset")) .default_value({0.0f, 0.0f, 1.0f}) - .subtype(PROP_TRANSLATION); + .subtype(PROP_TRANSLATION) + .description(N_( + "In offset mode, the distance between each socket on each axis. In end points mode, the " + "position of the final vertex")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 54a762fc15d..3197a94c27b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -29,9 +29,21 @@ namespace blender::nodes { static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>(N_("Segments")).default_value(32).min(3).max(1024); - b.add_input<decl::Int>(N_("Rings")).default_value(16).min(2).max(1024); - b.add_input<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>(N_("Segments")) + .default_value(32) + .min(3) + .max(1024) + .description(N_("Horizontal resolution of the sphere")); + b.add_input<decl::Int>(N_("Rings")) + .default_value(16) + .min(2) + .max(1024) + .description(N_("The number of horizontal rings")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); b.add_output<decl::Geometry>(N_("Mesh")); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 312ea7df919..18d674a38a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -222,16 +222,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); BKE_volume_init_grids(volume); - VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); - openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>( - BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); - const float density = params.get_input<float>("Density"); convert_to_grid_index_space(voxel_size, positions, radii); openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); - /* This merge is cheap, because the #density_grid is empty. */ - density_grid->merge(*new_grid); - density_grid->transform().postScale(voxel_size); + new_grid->transform().postScale(voxel_size); + BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid)); + r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); r_geometry_set.replace_volume(volume); } diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 27c8aa532f2..2bb50d5efbe 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1079,6 +1079,10 @@ typedef struct wmDrag { /** * Dropboxes are like keymaps, part of the screen/area/region definition. * Allocation and free is on startup and exit. + * + * The operator is polled and invoked with the current context (#WM_OP_INVOKE_DEFAULT), there is no + * way to override that (by design, since dropboxes should act on the exact mouse position). So the + * drop-boxes are supposed to check the required area and region context in their poll. */ typedef struct wmDropBox { struct wmDropBox *next, *prev; @@ -1120,10 +1124,6 @@ typedef struct wmDropBox { struct IDProperty *properties; /** RNA pointer to access properties. */ struct PointerRNA *ptr; - - /** Default invoke. */ - short opcontext; - } wmDropBox; /** diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 4458b386ab6..47ee296823b 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -91,13 +91,18 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (win->workspace_hook != NULL) { ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); - BKE_LIB_FOREACHID_PROCESS_ID(data, workspace, IDWALK_CB_NOP); + BKE_lib_query_foreachid_process(data, &workspace, IDWALK_CB_USER); /* Allow callback to set a different workspace. */ BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; + } } + if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) { - BKE_screen_foreach_id_screen_area(data, area); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_screen_foreach_id_screen_area(data, area)); } } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 85378ffd7c7..648ad2bb8fe 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -122,7 +122,6 @@ wmDropBox *WM_dropbox_add(ListBase *lb, drop->cancel = cancel; drop->tooltip = tooltip; drop->ot = WM_operatortype_find(idname, 0); - drop->opcontext = WM_OP_INVOKE_DEFAULT; if (drop->ot == NULL) { MEM_freeN(drop); @@ -324,7 +323,8 @@ static wmDropBox *dropbox_active(bContext *C, continue; } - if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + const int opcontext = wm_drop_operator_context_get(drop); + if (WM_operator_poll_context(C, drop->ot, opcontext)) { return drop; } @@ -392,10 +392,11 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) { + const int opcontext = wm_drop_operator_context_get(drop); /* Optionally copy drag information to operator properties. Don't call it if the * operator fails anyway, it might do more than just set properties (e.g. * typically import an asset). */ - if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + if (drop->copy && WM_operator_poll_context(C, drop->ot, opcontext)) { drop->copy(drag, drop); } @@ -423,6 +424,16 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) } } +/** + * The operator of a dropbox should always be executed in the context determined by the mouse + * coordinates. The dropbox poll should check the context area and region as needed. + * So this always returns #WM_OP_INVOKE_DEFAULT. + */ +int wm_drop_operator_context_get(const wmDropBox *UNUSED(drop)) +{ + return WM_OP_INVOKE_DEFAULT; +} + /* ************** IDs ***************** */ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 8acce240046..d8d57a9370c 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -108,6 +108,8 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region) } if (pc->poll == NULL || pc->poll(C)) { + UI_SetTheme(area->spacetype, region->regiontype); + /* Prevent drawing outside region. */ GPU_scissor_test(true); GPU_scissor(region->winrct.xmin, @@ -839,6 +841,7 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) } /* After area regions so we can do area 'overlay' drawing. */ + UI_SetTheme(0, 0); ED_screen_draw_edges(win); wm_draw_callbacks(win); wmWindowViewport(win); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 798f60fba3d..f28a637bcd7 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3063,8 +3063,9 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis BLI_addtail(&single_lb, drag); event->customdata = &single_lb; + const int opcontext = wm_drop_operator_context_get(drop); int op_retval = wm_operator_call_internal( - C, drop->ot, drop->ptr, NULL, drop->opcontext, false, event); + C, drop->ot, drop->ptr, NULL, opcontext, false, event); OPERATOR_RETVAL_CHECK(op_retval); if ((op_retval & OPERATOR_CANCELLED) && drop->cancel) { diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 97e610b797d..c3e0764f6c2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -812,7 +812,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_assert(!ID_IS_LINKED(id)); - BKE_libblock_relink_to_newid_new(bmain, id); + BKE_libblock_relink_to_newid(bmain, id, 0); } /* Remove linked IDs when a local existing data has been reused instead. */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index fa21dbcb4bb..a41fa94e8c2 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -1845,7 +1845,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } IMB_exit(); - BKE_images_exit(); DEG_free_node_types(); totblock = MEM_get_memory_blocks_in_use(); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 40e4d905fcd..36663513496 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -175,6 +175,7 @@ void wm_dropbox_free(void); void wm_drags_exit(wmWindowManager *wm, wmWindow *win); void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop); void wm_drags_check_ops(bContext *C, const wmEvent *event); +int wm_drop_operator_context_get(const wmDropBox *drop); void wm_drags_draw(bContext *C, wmWindow *win); #ifdef __cplusplus |