diff options
Diffstat (limited to 'source/blender')
468 files changed, 12878 insertions, 4215 deletions
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index 4bcd27082c0..4c2e72418a0 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -56,11 +56,6 @@ if(WIN32) target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib) set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt") - install( - FILES $<TARGET_FILE:BlendThumb> - COMPONENT Blender - DESTINATION "." - ) else() # ----------------------------------------------------------------------------- # Build `blender-thumbnailer` executable @@ -68,10 +63,4 @@ else() add_executable(blender-thumbnailer ${SRC} src/blender_thumbnailer.cc) target_link_libraries(blender-thumbnailer bf_blenlib) target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES}) - - install( - FILES $<TARGET_FILE:blender-thumbnailer> - COMPONENT Blender - DESTINATION "." - ) endif() diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 27478bd7f8e..90c8d6357de 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -297,44 +297,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 +327,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 +352,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 +364,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 +395,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 +405,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 +442,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 +457,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 +559,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 +587,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 +610,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 +630,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 +645,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 +684,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 +695,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 +723,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) { @@ -874,7 +829,7 @@ float blf_font_fixed_width(FontBLF *font) 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); + g = blf_glyph_ensure(font, gc, FT_Get_Char_Index(font->face, c)); /* if we don't find the glyph. */ if (!g) { @@ -896,7 +851,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 +863,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 +882,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } g_prev = g; - c_prev = c; } if (r_info) { @@ -978,7 +928,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 +948,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). @@ -1047,14 +993,12 @@ static void blf_font_wrap_apply(FontBLF *font, pen_x = 0; pen_y -= gc->glyph_height_max; 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); diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 6cdf5fc5996..9170a1c0ac4 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -175,33 +175,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 +203,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 +221,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 +240,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 +258,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 +417,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..ba871ea2496 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -140,13 +140,10 @@ 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/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 42eea41b7a7..722d142b56c 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -20,6 +20,7 @@ #pragma once +#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" #include "DNA_asset_types.h" @@ -29,11 +30,23 @@ extern "C" { #endif struct AssetLibraryReference; +struct AssetMetaData; struct BlendDataReader; struct BlendWriter; struct ID; +struct IDProperty; struct PreviewImage; +typedef void (*PreSaveFn)(void *asset_ptr, struct AssetMetaData *asset_data); + +typedef struct AssetTypeInfo { + /** + * For local assets (assets in the current .blend file), a callback to execute before the file is + * saved. + */ + PreSaveFn pre_save_fn; +} AssetTypeInfo; + struct AssetMetaData *BKE_asset_metadata_create(void); void BKE_asset_metadata_free(struct AssetMetaData **asset_data); @@ -56,6 +69,10 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref); +void BKE_asset_metadata_idprop_ensure(struct AssetMetaData *asset_data, struct IDProperty *prop); +struct IDProperty *BKE_asset_metadata_idprop_find(const struct AssetMetaData *asset_data, + const char *name) ATTR_WARN_UNUSED_RESULT; + struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, const struct ID *owner_id); diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 4a1c29badb4..cf19eb29a0e 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -183,6 +183,8 @@ struct WriteAttributeLookup { GVMutableArrayPtr varray; /* Domain the attributes lives on in the geometry. */ AttributeDomain domain; + /* Call this after changing the attribute to invalidate caches that depend on this attribute. */ + std::function<void()> tag_modified_fn; /* Convenience function to check if the attribute has been found. */ operator bool() const @@ -208,7 +210,7 @@ class OutputAttribute { private: GVMutableArrayPtr varray_; - AttributeDomain domain_; + AttributeDomain domain_ = ATTR_DOMAIN_AUTO; SaveFn save_; std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_; bool ignore_old_values_ = false; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 3f8b56b2736..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ 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. */ @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 38 +#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_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh index 87bec6203a9..fb077425336 100644 --- a/source/blender/blenkernel/BKE_curve_to_mesh.hh +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -25,7 +25,7 @@ struct CurveEval; namespace blender::bke { -Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile); +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps); Mesh *curve_to_wire_mesh(const CurveEval &curve); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index f57765e373b..58a89d0207a 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -318,6 +318,9 @@ struct GeometrySet { bool include_instances, blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const; + blender::Vector<GeometryComponentType> gather_component_types(bool include_instances, + bool ignore_empty) const; + using ForeachSubGeometryCallback = blender::FunctionRef<void(GeometrySet &geometry_set)>; void modify_geometry_sets(ForeachSubGeometryCallback callback); @@ -621,7 +624,9 @@ class InstancesComponent : public GeometryComponent { blender::Vector<blender::float4x4> instance_transforms_; /** * IDs of the instances. They are used for consistency over multiple frames for things like - * motion blur. + * motion blur. Proper stable ID data that actually helps when rendering can only be generated + * in some situations, so this vector is allowed to be empty, in which case the index of each + * instance will be used for the final ID. */ blender::Vector<int> instance_ids_; @@ -643,7 +648,7 @@ class InstancesComponent : public GeometryComponent { void resize(int capacity); int add_reference(const InstanceReference &reference); - void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); + void add_instance(int instance_handle, const blender::float4x4 &transform); blender::Span<InstanceReference> references() const; void remove_unused_references(); @@ -658,6 +663,9 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan<int> instance_ids(); blender::Span<int> instance_ids() const; + blender::MutableSpan<int> instance_ids_ensure(); + void instance_ids_clear(); + int instances_amount() const; int references_amount() const; @@ -736,6 +744,7 @@ class AttributeFieldInput : public fn::FieldInput { AttributeFieldInput(std::string name, const CPPType &type) : fn::FieldInput(type, name), name_(std::move(name)) { + category_ = Category::NamedAttribute; } template<typename T> static fn::Field<T> Create(std::string name) @@ -764,10 +773,9 @@ class IDAttributeFieldInput : public fn::FieldInput { public: IDAttributeFieldInput() : fn::FieldInput(CPPType::get<int>()) { + category_ = Category::Generated; } - static fn::Field<int> Create(); - const GVArray *get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; @@ -785,18 +793,25 @@ class AnonymousAttributeFieldInput : public fn::FieldInput { * automatically. */ StrongAnonymousAttributeID anonymous_id_; + std::string producer_name_; public: - AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) - : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, + const CPPType &type, + std::string producer_name) + : fn::FieldInput(type, anonymous_id.debug_name()), + anonymous_id_(std::move(anonymous_id)), + producer_name_(producer_name) { + category_ = Category::AnonymousAttribute; } - template<typename T> static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id) + template<typename T> + static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name) { const CPPType &type = CPPType::get<T>(); - auto field_input = std::make_shared<AnonymousAttributeFieldInput>(std::move(anonymous_id), - type); + auto field_input = std::make_shared<AnonymousAttributeFieldInput>( + std::move(anonymous_id), type, std::move(producer_name)); return fn::Field<T>{field_input}; } diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index cd656d94fce..d33c24f2c75 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -228,6 +228,11 @@ typedef struct IDTypeInfo { * \note Currently needed for some update operation on point caches. */ IDTypeLibOverrideApplyPost lib_override_apply_post; + + /** + * Callbacks for assets, based on the type of asset. + */ + struct AssetTypeInfo *asset_type_info; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 9c49514e7b8..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,22 +155,33 @@ 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; \ } \ } \ ((void)0) -#define BKE_LIB_FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#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..8df6a803358 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -111,8 +111,7 @@ 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) 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/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 68b1b55f47f..9ded97e0003 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -244,9 +244,9 @@ void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_refe #define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \ { \ - ID *_id_next = (_lb)->first; \ + ID *_id_next = (ID *)(_lb)->first; \ for ((_id) = _id_next; (_id) != NULL; (_id) = _id_next) { \ - _id_next = (_id)->next; + _id_next = (ID *)(_id)->next; #define FOREACH_MAIN_LISTBASE_ID_END \ } \ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ef1e7249658..58fea6d462c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1442,7 +1442,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 #define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 -#define GEO_NODE_VOLUME_TO_MESH 1026 +#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026 #define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE_MESH 1029 @@ -1547,6 +1547,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_RAYCAST 1128 #define GEO_NODE_CURVE_TO_POINTS 1130 #define GEO_NODE_INSTANCES_TO_POINTS 1131 +#define GEO_NODE_IMAGE_TEXTURE 1132 +#define GEO_NODE_VOLUME_TO_MESH 1133 +#define GEO_NODE_INPUT_ID 1134 +#define GEO_NODE_SET_ID 1135 /** \} */ diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index bd887c1ea0d..fce2d3178aa 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -29,6 +29,9 @@ extern "C" { struct UserDef; struct bUserAssetLibrary; +/** Name of the asset library added by default. */ +#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME DATA_("User Library") + struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *path) ATTR_NONNULL(1); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 97e0d8415a5..8509b730709 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -570,6 +570,8 @@ struct CurveEval { blender::Array<int> evaluated_point_offsets() const; blender::Array<float> accumulated_spline_lengths() const; + void mark_cache_invalid(); + void assert_valid_point_attributes() const; }; diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index 5fe0d54c2cf..601e0cf26a9 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume, #ifdef __cplusplus # include "BLI_float3.hh" # include "BLI_float4x4.hh" +# include "BLI_string_ref.hh" bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max); @@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f # include <openvdb/openvdb.h> # include <openvdb/points/PointDataGrid.h> +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + blender::StringRef name, + openvdb::GridBase::Ptr vdb_grid); + bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, blender::float3 &r_min, blender::float3 &r_max); diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh index 1f6e89636c4..9532da8c23c 100644 --- a/source/blender/blenkernel/BKE_volume_to_mesh.hh +++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_span.hh" + #include "DNA_modifier_types.h" #ifdef WITH_OPENVDB @@ -33,10 +35,40 @@ struct VolumeToMeshResolution { }; #ifdef WITH_OPENVDB + +/** + * The result of converting a volume grid to mesh data, in the format used by the OpenVDB API. + */ +struct OpenVDBMeshData { + std::vector<openvdb::Vec3s> verts; + std::vector<openvdb::Vec3I> tris; + std::vector<openvdb::Vec4I> quads; + bool is_empty() const + { + return verts.empty(); + } +}; + struct Mesh *volume_to_mesh(const openvdb::GridBase &grid, const VolumeToMeshResolution &resolution, const float threshold, const float adaptivity); + +struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity); + +void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts, + const Span<openvdb::Vec3I> vdb_tris, + const Span<openvdb::Vec4I> vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan<MVert> verts, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops); + #endif } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 65900ec0f4b..cae72ddf68c 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -175,11 +175,11 @@ 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) { - BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 7e4ab754500..21887d514d9 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -294,11 +294,11 @@ 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(data, adt->action, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, adt->tmpact, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->tmpact, IDWALK_CB_USER); LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) { LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) { diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index ce4ab8a4ba1..fb6656a4b1c 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -259,23 +259,24 @@ 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; #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); + return (BLI_exists(dir)); } + return false; +#elif defined(__APPLE__) + const char *home = BLI_getenv("HOME"); + BLI_snprintf(dir, FILE_MAXDIR, "%s/Library/Fonts/", home); + return (BLI_exists(dir)); +#else + BLI_strncpy(dir, "/usr/share/fonts/", FILE_MAXDIR); + return (BLI_exists(dir)); #endif - /* TODO: Values for other platforms. */ - UNUSED_VARS(dir); - return success; } /** \} */ 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/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 2375c606247..a6d9a1f41e9 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -133,7 +133,7 @@ static double test_vec_roll_to_mat3_normalized(const float input[3], float roll_mat[3][3]; if (normalize) { - /* The vector is renormalized to replicate the actual usage. */ + /* The vector is re-normalized to replicate the actual usage. */ normalize_v3_v3(input_normalized, input); } else { diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index ae9ded3c754..7bea089b9bf 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -53,6 +53,7 @@ void BKE_asset_metadata_free(AssetMetaData **asset_data) if ((*asset_data)->properties) { IDP_FreeProperty((*asset_data)->properties); } + MEM_SAFE_FREE((*asset_data)->author); MEM_SAFE_FREE((*asset_data)->description); BLI_freelistN(&(*asset_data)->tags); @@ -140,6 +141,25 @@ void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length); } +void BKE_asset_metadata_idprop_ensure(AssetMetaData *asset_data, IDProperty *prop) +{ + if (!asset_data->properties) { + IDPropertyTemplate val = {0}; + asset_data->properties = IDP_New(IDP_GROUP, &val, "AssetMetaData.properties"); + } + /* Important: The property may already exist. For now just allow always allow a newly allocated + * property, and replace the existing one as a way of updating. */ + IDP_ReplaceInGroup(asset_data->properties, prop); +} + +IDProperty *BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) +{ + if (!asset_data->properties) { + return nullptr; + } + return IDP_GetPropertyFromGroup(asset_data->properties, name); +} + /* Queries -------------------------------------------- */ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), @@ -158,6 +178,9 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) IDP_BlendWrite(writer, asset_data->properties); } + if (asset_data->author) { + BLO_write_string(writer, asset_data->author); + } if (asset_data->description) { BLO_write_string(writer, asset_data->description); } @@ -169,12 +192,14 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) { /* asset_data itself has been read already. */ + asset_data->local_type_info = nullptr; if (asset_data->properties) { BLO_read_data_address(reader, &asset_data->properties); IDP_BlendDataRead(reader, &asset_data->properties); } + BLO_read_data_address(reader, &asset_data->author); BLO_read_data_address(reader, &asset_data->description); BLO_read_list(reader, &asset_data->tags); BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc index fec2b76e7a1..20cac76b40b 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -197,6 +197,8 @@ void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const for (const char *path_component = this->path_.data(); path_component && path_component[0]; /* Jump to one after the next slash if there is any. */ path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + /* Note that this also treats backslashes as component separators, which + * helps in cleaning up backslash-separated paths. */ next_slash_ptr = BLI_path_slash_find(path_component); const bool is_last_component = next_slash_ptr == nullptr; diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc index be50f2fc001..f248863ce77 100644 --- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -193,19 +193,33 @@ TEST(AssetCatalogPathTest, is_contained_in) TEST(AssetCatalogPathTest, cleanup) { - AssetCatalogPath ugly_path("/ some / родитель / "); - AssetCatalogPath clean_path = ugly_path.cleanup(); - - EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) - << "cleanup should not modify the path instance itself"; - - EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); - - AssetCatalogPath double_slashed("some//родитель"); - EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); - - AssetCatalogPath with_colons("some/key:subkey=value/path"); - EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); + { + AssetCatalogPath ugly_path("/ some / родитель / "); + AssetCatalogPath clean_path = ugly_path.cleanup(); + EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) + << "cleanup should not modify the path instance itself"; + EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); + } + { + AssetCatalogPath double_slashed("some//родитель"); + EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); + } + { + AssetCatalogPath with_colons("some/key:subkey=value/path"); + EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); + } + { + const AssetCatalogPath with_backslashes("windows\\for\\life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_backslashes.cleanup()); + } + { + const AssetCatalogPath with_mixed("windows\\for/life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_mixed.cleanup()); + } + { + const AssetCatalogPath with_punctuation("is!/this?/¿valid?"); + EXPECT_EQ(AssetCatalogPath("is!/this?/¿valid?"), with_punctuation.cleanup()); + } } TEST(AssetCatalogPathTest, iterate_components) diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc index abe6d384247..2cef34966f8 100644 --- a/source/blender/blenkernel/intern/asset_catalog_test.cc +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -902,8 +902,6 @@ TEST_F(AssetCatalogTest, update_catalog_path) const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); ASSERT_NE(nullptr, renamed_cat); ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog."; - EXPECT_EQ(orig_cat->simple_name, renamed_cat->simple_name) - << "Changing the path should not change the simple name."; EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) << "Changing the path should not change the catalog ID."; @@ -932,6 +930,47 @@ TEST_F(AssetCatalogTest, update_catalog_path_simple_name) << "Changing the path should update the simplename of children."; } +TEST_F(AssetCatalogTest, update_catalog_path_add_slashes) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); + const AssetCatalogPath orig_path = orig_cat->path; + + /* Original path is `character/Ružena/poselib`. + * This rename will also create a new catalog for `character/Ružena/poses`. */ + service.update_catalog_path(UUID_POSES_RUZENA, "character/Ružena/poses/general"); + + EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path)) + << "The original (pre-rename) path should not be associated with a catalog any more."; + + const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, renamed_cat); + EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) + << "Changing the path should not change the catalog ID."; + + EXPECT_EQ("character/Ružena/poses/general", renamed_cat->path.str()) + << "When creating a new catalog by renaming + adding a slash, the renamed catalog should be " + "assigned the path passed to update_catalog_path()"; + + /* Test the newly created catalog. */ + const AssetCatalog *new_cat = service.find_catalog_by_path("character/Ružena/poses"); + ASSERT_NE(nullptr, new_cat) << "Renaming to .../X/Y should cause .../X to exist as well."; + EXPECT_EQ("character/Ružena/poses", new_cat->path.str()); + EXPECT_EQ("character-Ružena-poses", new_cat->simple_name); + EXPECT_TRUE(new_cat->flags.has_unsaved_changes); + + /* Test the children. */ + EXPECT_EQ("character/Ružena/poses/general/hand", + service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str()) + << "Changing the path should update children."; + EXPECT_EQ("character/Ružena/poses/general/face", + service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str()) + << "Changing the path should update children."; +} + TEST_F(AssetCatalogTest, merge_catalog_files) { const CatalogFilePath cdf_dir = create_temp_path(); diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc index 80504bbdc05..e26ae05301e 100644 --- a/source/blender/blenkernel/intern/asset_library_service_test.cc +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -23,6 +23,7 @@ #include "BLI_path_util.h" #include "BKE_appdir.h" +#include "BKE_callbacks.h" #include "CLG_log.h" @@ -40,10 +41,12 @@ class AssetLibraryServiceTest : public testing::Test { static void SetUpTestSuite() { CLG_init(); + BKE_callback_global_init(); } static void TearDownTestSuite() { CLG_exit(); + BKE_callback_global_finalize(); } void SetUp() override diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index c6c949a7ec4..702008fed96 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -20,6 +20,7 @@ #include "BKE_appdir.h" #include "BKE_asset_catalog.hh" #include "BKE_asset_library.hh" +#include "BKE_callbacks.h" #include "asset_library_service.hh" @@ -34,10 +35,12 @@ class AssetLibraryTest : public testing::Test { static void SetUpTestSuite() { CLG_init(); + BKE_callback_global_init(); } static void TearDownTestSuite() { CLG_exit(); + BKE_callback_global_finalize(); } void TearDown() override diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 3386d346364..01b53baf237 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -360,7 +360,7 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( return as_read_attribute_(data, domain_size); } -GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( +WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( GeometryComponent &component) const { if (writable_ != Writable) { @@ -397,10 +397,14 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( data = new_data; } + std::function<void()> tag_modified_fn; if (update_on_write_ != nullptr) { - update_on_write_(component); + tag_modified_fn = [component = &component, update = update_on_write_]() { + update(*component); + }; } - return as_write_attribute_(data, domain_size); + + return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; } bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const @@ -925,7 +929,7 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + return builtin_provider->try_get_for_write(*this); } } for (const DynamicAttributesProvider *dynamic_provider : @@ -1249,6 +1253,20 @@ static void save_output_attribute(OutputAttribute &output_attribute) varray.get(i, buffer); write_attribute.varray->set_by_relocate(i, buffer); } + if (write_attribute.tag_modified_fn) { + write_attribute.tag_modified_fn(); + } +} + +static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method( + const blender::bke::WriteAttributeLookup &attribute) +{ + if (!attribute.tag_modified_fn) { + return {}; + } + return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) { + tag_modified_fn(); + }; } static OutputAttribute create_output_attribute(GeometryComponent &component, @@ -1293,14 +1311,21 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Builtin attribute is on different domain. */ return {}; } + GVMutableArrayPtr varray = std::move(attribute.varray); if (varray->type() == *cpp_type) { /* Builtin attribute matches exactly. */ - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Builtin attribute is on the same domain but has a different data type. */ varray = conversions.try_convert(std::move(varray), *cpp_type); - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } const int domain_size = component.attribute_domain_size(domain); @@ -1324,7 +1349,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, } if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { /* Existing generic attribute matches exactly. */ - return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); + + return OutputAttribute(std::move(attribute.varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing @@ -1385,7 +1414,7 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex std::string AttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Attribute: ") << name_; + ss << '"' << name_ << '"' << TIP_(" attribute from geometry"); return ss.str(); } @@ -1468,7 +1497,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( std::string AnonymousAttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Anonymous Attribute: ") << debug_name_; + ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_; return ss.str(); } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 5cedcf69953..140498bdb01 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -87,7 +87,7 @@ class BuiltinAttributeProvider { } virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const AttributeInit &UNUSED(initializer)) const = 0; @@ -267,7 +267,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { } GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 9facb146361..dc3c2a8e55e 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -207,14 +207,15 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) { Brush *brush = (Brush *)id; - BKE_LIB_FOREACHID_PROCESS(data, brush->toggle_brush, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->clone.image, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->paint_curve, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->toggle_brush, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->clone.image, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER); if (brush->gpencil_settings) { - BKE_LIB_FOREACHID_PROCESS(data, brush->gpencil_settings->material, IDWALK_CB_USER); + 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/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index dbc213907ac..72dd51a940d 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -30,11 +30,20 @@ static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}}; +static bool callbacks_initialized = false; + +#define ASSERT_CALLBACKS_INITIALIZED() \ + BLI_assert_msg(callbacks_initialized, \ + "Callbacks should be initialized with BKE_callback_global_init() before using " \ + "the callback system.") + void BKE_callback_exec(struct Main *bmain, struct PointerRNA **pointers, const int num_pointers, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); + /* Use mutable iteration so handlers are able to remove themselves. */ ListBase *lb = &callback_slots[evt]; LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) { @@ -75,18 +84,26 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); ListBase *lb = &callback_slots[evt]; BLI_addtail(lb, funcstore); } void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) { - ListBase *lb = &callback_slots[evt]; - - /* Be safe, as the callback may have already been removed by BKE_callback_global_finalize(), for + /* The callback may have already been removed by BKE_callback_global_finalize(), for * example when removing callbacks in response to a BKE_blender_atexit_register callback * function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */ - BLI_remlink_safe(lb, funcstore); + if (!callbacks_initialized) { + return; + } + + ListBase *lb = &callback_slots[evt]; + + /* Be noisy about potential programming errors. */ + BLI_assert_msg(BLI_findindex(lb, funcstore) != -1, "To-be-removed callback not found"); + + BLI_remlink(lb, funcstore); if (funcstore->alloc) { MEM_freeN(funcstore); @@ -95,7 +112,7 @@ void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) void BKE_callback_global_init(void) { - /* do nothing */ + callbacks_initialized = true; } /* call on application exit */ @@ -111,4 +128,6 @@ void BKE_callback_global_finalize(void) BKE_callback_remove(funcstore, evt); } } + + callbacks_initialized = false; } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index ed1f6fcb40a..9455eed7f3f 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -103,13 +103,13 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data) { Camera *camera = (Camera *)id; - BKE_LIB_FOREACHID_PROCESS(data, camera->dof.focus_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, camera->dof.focus_object, IDWALK_CB_NOP); LISTBASE_FOREACH (CameraBGImage *, bgpic, &camera->bg_images) { if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->ima, IDWALK_CB_USER); } else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->clip, IDWALK_CB_USER); } } } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 8e50b9e9534..14097ecd8a7 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -158,10 +158,11 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) Collection *collection = (Collection *)id; LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - BKE_LIB_FOREACHID_PROCESS(data, cob->ob, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - BKE_LIB_FOREACHID_PROCESS(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) { /* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad @@ -170,7 +171,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) (parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag); } } @@ -715,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); #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/curve.c b/source/blender/blenkernel/intern/curve.c index 620110f7881..aae9ac383a4 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -130,17 +130,17 @@ static void curve_free_data(ID *id) static void curve_foreach_id(ID *id, LibraryForeachIDData *data) { Curve *curve = (Curve *)id; - BKE_LIB_FOREACHID_PROCESS(data, curve->bevobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->taperobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->textoncurve, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->bevobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->taperobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->textoncurve, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->key, IDWALK_CB_USER); for (int i = 0; i < curve->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, curve->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->mat[i], IDWALK_CB_USER); } - BKE_LIB_FOREACHID_PROCESS(data, curve->vfont, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontb, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfonti, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontbi, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfont, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontb, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfonti, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontbi, IDWALK_CB_USER); } static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 8eec7f5dfab..0e3da9e0789 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -160,6 +160,13 @@ blender::Array<float> CurveEval::accumulated_spline_lengths() const return spline_lengths; } +void CurveEval::mark_cache_invalid() +{ + for (SplinePtr &spline : splines_) { + spline->mark_cache_invalid(); + } +} + static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 5f2f945192c..cd40d5e8a41 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -88,6 +88,7 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges) } static void spline_extrude_to_mesh_data(const ResultInfo &info, + const bool fill_caps, MutableSpan<MVert> r_verts, MutableSpan<MEdge> r_edges, MutableSpan<MLoop> r_loops, @@ -180,6 +181,37 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } } + if (fill_caps && profile.is_cyclic()) { + const int poly_size = info.spline_edge_len * info.profile_edge_len; + const int cap_loop_offset = info.loop_offset + poly_size * 4; + const int cap_poly_offset = info.poly_offset + poly_size; + + MPoly &poly_start = r_polys[cap_poly_offset]; + poly_start.loopstart = cap_loop_offset; + poly_start.totloop = info.profile_edge_len; + MPoly &poly_end = r_polys[cap_poly_offset + 1]; + poly_end.loopstart = cap_loop_offset + info.profile_edge_len; + poly_end.totloop = info.profile_edge_len; + + const int last_ring_index = info.spline_vert_len - 1; + const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index; + const int last_ring_edge_offset = profile_edges_start + + info.profile_edge_len * last_ring_index; + + for (const int i : IndexRange(info.profile_edge_len)) { + const int i_inv = info.profile_edge_len - i - 1; + MLoop &loop_start = r_loops[cap_loop_offset + i]; + loop_start.v = info.vert_offset + i_inv; + loop_start.e = profile_edges_start + i_inv; + MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i]; + loop_end.v = last_ring_vert_offset + i; + loop_end.e = last_ring_edge_offset + i; + } + + mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len)); + mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len)); + } + /* Calculate the positions of each profile ring profile along the spline. */ Span<float3> positions = spline.evaluated_positions(); Span<float3> tangents = spline.evaluated_tangents(); @@ -226,14 +258,22 @@ static inline int spline_extrude_edge_size(const Spline &curve, const Spline &pr curve.evaluated_edges_size() * profile.evaluated_points_size(); } -static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_loop_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + return tube + caps; } -static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) +static inline int spline_extrude_poly_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) { - return curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + return tube + caps; } struct ResultOffsets { @@ -242,7 +282,9 @@ struct ResultOffsets { Array<int> loop; Array<int> poly; }; -static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<SplinePtr> curves) +static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, + Span<SplinePtr> curves, + const bool fill_caps) { const int total = profiles.size() * curves.size(); Array<int> vert(total + 1); @@ -263,8 +305,8 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl poly[mesh_index] = poly_offset; vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); - loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]); - poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]); + loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile], fill_caps); + poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile], fill_caps); mesh_index++; } } @@ -652,12 +694,12 @@ static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, * changed anyway in a way that affects the normals. So currently this code uses the safer / * simpler solution of deferring normal calculation to the rest of Blender. */ -Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile) +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps) { Span<SplinePtr> profiles = profile.splines(); Span<SplinePtr> curves = curve.splines(); - const ResultOffsets offsets = calculate_result_offsets(profiles, curves); + const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps); if (offsets.vert.last() == 0) { return nullptr; } @@ -696,6 +738,7 @@ Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile) }; spline_extrude_to_mesh_data(info, + fill_caps, {mesh->mvert, mesh->totvert}, {mesh->medge, mesh->totedge}, {mesh->mloop, mesh->totloop}, @@ -733,7 +776,7 @@ static CurveEval get_curve_single_vert() Mesh *curve_to_wire_mesh(const CurveEval &curve) { static const CurveEval vert_curve = get_curve_single_vert(); - return curve_to_mesh_sweep(curve, vert_curve); + return curve_to_mesh_sweep(curve, vert_curve, false); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 8e9c504dcbf..bbf61c51bfb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -203,14 +203,18 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) switch (fcm->type) { case FMODIFIER_TYPE_PYTHON: { FMod_Python *fcm_py = (FMod_Python *)fcm->data; - BKE_LIB_FOREACHID_PROCESS(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_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP); + + 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_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index f30ff2a70a7..d3c3fcc1e67 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -433,7 +433,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { return as_read_attribute_(*curve); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { if (writable_ != Writable) { return {}; @@ -442,7 +442,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { if (curve == nullptr) { return {}; } - return as_write_attribute_(*curve); + return {as_write_attribute_(*curve), domain_}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -1122,7 +1122,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_gvarray(spans, offsets); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1133,22 +1133,30 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return {}; } + std::function<void()> tag_modified_fn; + if (update_on_write_ != nullptr) { + tag_modified_fn = [curve, update = update_on_write_]() { + for (SplinePtr &spline : curve->splines()) { + update(*spline); + } + }; + } + MutableSpan<SplinePtr> splines = curve->splines(); if (splines.size() == 1) { - return std::make_unique<fn::GVMutableArray_For_GMutableSpan>( - get_mutable_span_(*splines.first())); + return {std::make_unique<fn::GVMutableArray_For_GMutableSpan>( + get_mutable_span_(*splines.first())), + domain_, + std::move(tag_modified_fn)}; } Array<int> offsets = curve->control_point_offsets(); Array<MutableSpan<T>> spans(splines.size()); for (const int i : splines.index_range()) { spans[i] = get_mutable_span_(*splines[i]); - if (update_on_write_) { - update_on_write_(*splines[i]); - } } - return point_data_gvarray(spans, offsets); + return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn}; } bool try_delete(GeometryComponent &component) const final @@ -1220,7 +1228,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo { } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1233,16 +1241,19 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); } - /* Changing the positions requires recalculation of cached evaluated data in many cases. - * This could set more specific flags in the future to avoid unnecessary recomputation. */ - for (SplinePtr &spline : curve->splines()) { - spline->mark_cache_invalid(); - } + auto tag_modified_fn = [curve]() { + /* Changing the positions requires recalculation of cached evaluated data in many cases. + * This could set more specific flags in the future to avoid unnecessary recomputation. */ + curve->mark_cache_invalid(); + }; Array<int> offsets = curve->control_point_offsets(); - return std::make_unique< - fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>( - offsets.last(), curve->splines(), std::move(offsets)); + return {std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float3, + VMutableArray_For_SplinePosition>>( + offsets.last(), curve->splines(), std::move(offsets)), + domain_, + tag_modified_fn}; } }; @@ -1278,7 +1289,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { offsets.last(), curve->splines(), std::move(offsets), is_right_); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1289,10 +1300,15 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } + auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); }; + Array<int> offsets = curve->control_point_offsets(); - return std::make_unique< - fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>( - offsets.last(), curve->splines(), std::move(offsets), is_right_); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_BezierHandles>>( + offsets.last(), curve->splines(), std::move(offsets), is_right_), + domain_, + tag_modified_fn}; } bool try_delete(GeometryComponent &UNUSED(component)) const final diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 4204d62e1a7..5fe77000519 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -60,7 +60,9 @@ void InstancesComponent::reserve(int min_capacity) { instance_reference_handles_.reserve(min_capacity); instance_transforms_.reserve(min_capacity); - instance_ids_.reserve(min_capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } /** @@ -73,7 +75,9 @@ void InstancesComponent::resize(int capacity) { instance_reference_handles_.resize(capacity); instance_transforms_.resize(capacity); - instance_ids_.resize(capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } void InstancesComponent::clear() @@ -85,15 +89,15 @@ void InstancesComponent::clear() references_.clear(); } -void InstancesComponent::add_instance(const int instance_handle, - const float4x4 &transform, - const int id) +void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) { BLI_assert(instance_handle >= 0); BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - instance_ids_.append(id); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } blender::Span<int> InstancesComponent::instance_reference_handles() const @@ -125,6 +129,22 @@ blender::Span<int> InstancesComponent::instance_ids() const } /** + * Make sure the ID storage size matches the number of instances. By directly resizing the + * component's vectors internally, it is possible to be in a situation where the IDs are not + * empty but they do not have the correct size; this function resolves that. + */ +blender::MutableSpan<int> InstancesComponent::instance_ids_ensure() +{ + instance_ids_.append_n_times(0, this->instances_amount() - instance_ids_.size()); + return instance_ids_; +} + +void InstancesComponent::instance_ids_clear() +{ + instance_ids_.clear_and_make_inline(); +} + +/** * With write access to the instances component, the data in the instanced geometry sets can be * changed. This is a function on the component rather than each reference to ensure `const` * correctness for that reason. @@ -327,8 +347,16 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) blender::Span<int> InstancesComponent::almost_unique_ids() const { std::lock_guard lock(almost_unique_ids_mutex_); - if (almost_unique_ids_.size() != instance_ids_.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + if (instance_ids().is_empty()) { + almost_unique_ids_.reinitialize(this->instances_amount()); + for (const int i : almost_unique_ids_.index_range()) { + almost_unique_ids_[i] = i; + } + } + else { + if (almost_unique_ids_.size() != instance_ids_.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + } } return almost_unique_ids_; } @@ -370,15 +398,16 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider transforms); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { InstancesComponent &instances_component = static_cast<InstancesComponent &>(component); MutableSpan<float4x4> transforms = instances_component.instance_transforms(); - return std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4, - float3, - get_transform_position, - set_transform_position>>( - transforms); + return { + std::make_unique<fn::GVMutableArray_For_DerivedSpan<float4x4, + float3, + get_transform_position, + set_transform_position>>(transforms), + domain_}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -398,11 +427,83 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider } }; +class InstanceIDAttributeProvider final : public BuiltinAttributeProvider { + public: + InstanceIDAttributeProvider() + : BuiltinAttributeProvider( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32, Creatable, Writable, Deletable) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast<const InstancesComponent &>(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return std::make_unique<fn::GVArray_For_Span<int>>(instances.instance_ids()); + } + + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast<InstancesComponent &>(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return {std::make_unique<fn::GVMutableArray_For_MutableSpan<int>>(instances.instance_ids()), + domain_}; + } + + bool try_delete(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast<InstancesComponent &>(component); + if (instances.instance_ids().is_empty()) { + return false; + } + instances.instance_ids_clear(); + return true; + } + + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + { + InstancesComponent &instances = static_cast<InstancesComponent &>(component); + if (instances.instances_amount() == 0) { + return false; + } + MutableSpan<int> ids = instances.instance_ids_ensure(); + switch (initializer.type) { + case AttributeInit::Type::Default: { + ids.fill(0); + break; + } + case AttributeInit::Type::VArray: { + const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), ids.data()); + break; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast<const AttributeInitMove &>(initializer).data; + ids.copy_from({static_cast<int *>(source_data), instances.instances_amount()}); + MEM_freeN(source_data); + break; + } + } + return true; + } + + bool exists(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast<const InstancesComponent &>(component); + return !instances.instance_ids().is_empty(); + } +}; + static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; + static InstanceIDAttributeProvider id; - return ComponentAttributeProviders({&position}, {}); + return ComponentAttributeProviders({&position, &id}, {}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 6091d3f3dab..c3e39c0b2cb 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1210,7 +1210,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 4753a9e0768..cd1bafe445a 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -527,6 +527,40 @@ void GeometrySet::gather_attributes_for_propagation( delete dummy_component; } +static void gather_component_types_recursive(const GeometrySet &geometry_set, + const bool include_instances, + const bool ignore_empty, + Vector<GeometryComponentType> &r_types) +{ + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + if (ignore_empty) { + if (component->is_empty()) { + continue; + } + } + r_types.append_non_duplicates(component->type()); + } + if (!include_instances) { + return; + } + const InstancesComponent *instances = geometry_set.get_component_for_read<InstancesComponent>(); + if (instances == nullptr) { + return; + } + instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { + gather_component_types_recursive( + instance_geometry_set, include_instances, ignore_empty, r_types); + }); +} + +blender::Vector<GeometryComponentType> GeometrySet::gather_component_types( + const bool include_instances, bool ignore_empty) const +{ + Vector<GeometryComponentType> types; + gather_component_types_recursive(*this, include_instances, ignore_empty, types); + return types; +} + static void gather_mutable_geometry_sets(GeometrySet &geometry_set, Vector<GeometrySet *> &r_geometry_sets) { diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index fa0741d3a2e..bea65030c06 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -139,11 +139,11 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) bGPdata *gpencil = (bGPdata *)id; /* materials */ for (int i = 0; i < gpencil->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, gpencil->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gpencil->mat[i], IDWALK_CB_USER); } LISTBASE_FOREACH (bGPDlayer *, gplayer, &gpencil->layers) { - BKE_LIB_FOREACHID_PROCESS(data, gplayer->parent, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gplayer->parent, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index cf346e9cac7..7433ee7ac29 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -107,7 +107,7 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data) { Hair *hair = (Hair *)id; for (int i = 0; i < hair->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, hair->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 97c742b1ec1..f820b345c59 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -359,22 +359,30 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id) PreviewImage **BKE_previewimg_id_get_p(const ID *id) { switch (GS(id->name)) { + case ID_OB: { + Object *ob = (Object *)id; + /* Currently, only object types with real geometry can be rendered as preview. */ + if (!OB_TYPE_IS_GEOMETRY(ob->type)) { + return nullptr; + } + return &ob->preview; + } + #define ID_PRV_CASE(id_code, id_struct) \ case id_code: { \ return &((id_struct *)id)->preview; \ } \ ((void)0) - ID_PRV_CASE(ID_MA, Material); - ID_PRV_CASE(ID_TE, Tex); - ID_PRV_CASE(ID_WO, World); - ID_PRV_CASE(ID_LA, Light); - ID_PRV_CASE(ID_IM, Image); - ID_PRV_CASE(ID_BR, Brush); - ID_PRV_CASE(ID_OB, Object); - ID_PRV_CASE(ID_GR, Collection); - ID_PRV_CASE(ID_SCE, Scene); - ID_PRV_CASE(ID_SCR, bScreen); - ID_PRV_CASE(ID_AC, bAction); + ID_PRV_CASE(ID_MA, Material); + ID_PRV_CASE(ID_TE, Tex); + ID_PRV_CASE(ID_WO, World); + ID_PRV_CASE(ID_LA, Light); + ID_PRV_CASE(ID_IM, Image); + ID_PRV_CASE(ID_BR, Brush); + ID_PRV_CASE(ID_GR, Collection); + ID_PRV_CASE(ID_SCE, Scene); + ID_PRV_CASE(ID_SCR, bScreen); + ID_PRV_CASE(ID_AC, bAction); #undef ID_PRV_CASE default: break; diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 5ae338aaaeb..3800cbec94b 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -211,8 +211,12 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *texture = image->gputexture[a][eye][resolution]; + if (texture == NULL) { + continue; + } key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); - key.cache_v = image->gputexture[a][eye]; + key.cache_v = texture; function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); } } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 9bca8172e64..a2da59bca58 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -131,7 +131,7 @@ static void lattice_free_data(ID *id) static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) { Lattice *lattice = (Lattice *)id; - BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lattice->key, IDWALK_CB_USER); } static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 2ac92828cec..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(&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 b5c45c0902b..248d85bcae0 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -669,56 +669,10 @@ void BKE_libblock_relink_ex( DEG_relations_tag_update(bmain); } +static void libblock_relink_to_newid(Main *bmain, ID *id); 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 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; } @@ -739,12 +693,22 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id); } } return IDWALK_RET_NOP; } +static void libblock_relink_to_newid(Main *bmain, ID *id) +{ + if (ID_IS_LINKED(id)) { + return; + } + + id->tag &= ~LIB_TAG_NEW; + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper, NULL, 0); +} + /** * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively * in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`. @@ -754,7 +718,7 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) * 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) { if (ID_IS_LINKED(id)) { return; @@ -762,6 +726,8 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) /* We do not want to have those cached relationship data here. */ BLI_assert(bmain->relations == NULL); - id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); + BKE_layer_collection_resync_forbid(); + libblock_relink_to_newid(bmain, id); + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync_remap(bmain); } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 36958e36004..1dba353d8ce 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -57,7 +57,7 @@ static void library_free_data(ID *id) static void library_foreach_id(ID *id, LibraryForeachIDData *data) { Library *lib = (Library *)id; - BKE_LIB_FOREACHID_PROCESS(data, lib->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->parent, IDWALK_CB_NEVER_SELF); } IDTypeInfo IDType_ID_LI = { 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/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 1f4abf36426..57ad6695db4 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -53,8 +53,8 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data) { LightProbe *probe = (LightProbe *)id; - BKE_LIB_FOREACHID_PROCESS(data, probe->image, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, probe->visibility_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->visibility_grp, IDWALK_CB_NOP); } static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index f4e4dd9f1ab..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) { @@ -168,7 +170,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -177,7 +179,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -186,7 +188,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleThicknessModifier_DistanceFromObject *p = (LineStyleThicknessModifier_DistanceFromObject *)lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 6c57d3139bb..5f726defb1a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -166,15 +166,14 @@ 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(data, material->texpaintslot->ima, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP); } if (material->gp_style != NULL) { - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->sima, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->sima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->ima, IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 6c8664aefed..48d31361eac 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -112,7 +112,7 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) { MetaBall *metaball = (MetaBall *)id; for (int i = 0; i < metaball->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, metaball->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index ed3766ad6a3..7277f7ad209 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -176,10 +176,10 @@ static void mesh_free_data(ID *id) static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) { Mesh *mesh = (Mesh *)id; - BKE_LIB_FOREACHID_PROCESS(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, mesh->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->key, IDWALK_CB_USER); for (int i = 0; i < mesh->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, mesh->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 7ac4c29f0ee..1c8646a4bdd 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -52,6 +52,8 @@ void BKE_mesh_runtime_reset(Mesh *mesh) memset(&mesh->runtime, 0, sizeof(mesh->runtime)); mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } /* Clear all pointers which we don't want to be shared on copying the datablock. @@ -71,6 +73,9 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) @@ -80,6 +85,11 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) MEM_freeN(mesh->runtime.eval_mutex); mesh->runtime.eval_mutex = NULL; } + if (mesh->runtime.render_mutex != NULL) { + BLI_mutex_end(mesh->runtime.render_mutex); + MEM_freeN(mesh->runtime.render_mutex); + mesh->runtime.render_mutex = NULL; + } if (mesh->runtime.mesh_eval != NULL) { mesh->runtime.mesh_eval->edit_mesh = NULL; BKE_id_free(NULL, mesh->runtime.mesh_eval); diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 0c2ac841b87..002f370cdcb 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -132,19 +132,19 @@ static void movie_clip_foreach_id(ID *id, LibraryForeachIDData *data) MovieClip *movie_clip = (MovieClip *)id; MovieTracking *tracking = &movie_clip->tracking; - BKE_LIB_FOREACHID_PROCESS(data, movie_clip->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, movie_clip->gpd, IDWALK_CB_USER); LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { LISTBASE_FOREACH (MovieTrackingTrack *, track, &object->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } } LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking->plane_tracks) { - BKE_LIB_FOREACHID_PROCESS(data, plane_track->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, plane_track->image, IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 487e925df79..124db07298d 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -488,14 +488,14 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker) */ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) { - BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER); + 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 eda57d9e984..55e8bdb2483 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -307,34 +307,36 @@ 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: { bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_COLLECTION: { bNodeSocketValueCollection *default_value = (bNodeSocketValueCollection *) sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_TEXTURE: { bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_MATERIAL: { bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_FLOAT: @@ -355,26 +357,30 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) { bNodeTree *ntree = (bNodeTree *)id; - BKE_LIB_FOREACHID_PROCESS(data, ntree->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER); 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)); } } @@ -4679,7 +4685,7 @@ static OutputFieldDependency find_group_output_dependencies( while (!sockets_to_check.is_empty()) { const InputSocketRef *input_socket = sockets_to_check.pop(); - for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { const NodeRef &origin_node = origin_socket->node(); const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; @@ -4717,10 +4723,10 @@ static OutputFieldDependency find_group_output_dependencies( static void propagate_data_requirements_from_right_to_left( const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) { - const Vector<const NodeRef *> sorted_nodes = tree.toposort( + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( NodeTreeRef::ToposortDirection::RightToLeft); - for (const NodeRef *node : sorted_nodes) { + for (const NodeRef *node : toposort_result.sorted_nodes) { const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( *node); @@ -4829,10 +4835,10 @@ static void determine_group_input_states( static void propagate_field_status_from_left_to_right( const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id) { - Vector<const NodeRef *> sorted_nodes = tree.toposort( + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( NodeTreeRef::ToposortDirection::LeftToRight); - for (const NodeRef *node : sorted_nodes) { + for (const NodeRef *node : toposort_result.sorted_nodes) { if (node->is_group_input_node()) { continue; } @@ -4848,14 +4854,14 @@ static void propagate_field_status_from_left_to_right( continue; } state.is_single = true; - if (input_socket->logically_linked_sockets().is_empty()) { + if (input_socket->directly_linked_sockets().is_empty()) { if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::Implicit) { state.is_single = false; } } else { - for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { if (!field_state_by_socket_id[origin_socket->id()].is_single) { state.is_single = false; break; @@ -5753,6 +5759,7 @@ static void registerGeometryNodes() register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_select_by_material(); register_node_type_geo_legacy_subdivision_surface(); + register_node_type_geo_legacy_volume_to_mesh(); register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_capture(); @@ -5800,8 +5807,10 @@ static void registerGeometryNodes() register_node_type_geo_delete_geometry(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); + register_node_type_geo_image_texture(); register_node_type_geo_input_curve_handles(); register_node_type_geo_input_curve_tilt(); + register_node_type_geo_input_id(); register_node_type_geo_input_index(); register_node_type_geo_input_material_index(); register_node_type_geo_input_material(); @@ -5850,6 +5859,7 @@ static void registerGeometryNodes() register_node_type_geo_set_curve_handles(); register_node_type_geo_set_curve_radius(); register_node_type_geo_set_curve_tilt(); + register_node_type_geo_set_id(); register_node_type_geo_set_material_index(); register_node_type_geo_set_material(); register_node_type_geo_set_point_radius(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e85c6b4c7c5..3b0825fb8db 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -82,6 +82,7 @@ #include "BKE_anim_visualization.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_asset.h" #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" @@ -388,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, @@ -397,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, @@ -406,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), @@ -416,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), @@ -425,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) @@ -452,11 +458,11 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } } - BKE_LIB_FOREACHID_PROCESS(data, object->parent, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->track, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); /* Special case! * Since this field is set/owned by 'user' of this ID (and not ID itself), @@ -468,22 +474,23 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) IDWALK_CB_INDIRECT_USAGE : 0, true); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } - BKE_LIB_FOREACHID_PROCESS(data, object->poselib, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); } /* Note that ob->gpd is deprecated, so no need to handle it here. */ - BKE_LIB_FOREACHID_PROCESS(data, object->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); if (object->pd) { - BKE_LIB_FOREACHID_PROCESS(data, object->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, object->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP); } /* Note that ob->effect is deprecated, so no need to handle it here. */ @@ -491,34 +498,52 @@ 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(data, pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, 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_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); } if (object->rigidbody_constraint) { - BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(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_IDSUPER( + data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); + } + + 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) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); if (object->soft->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->soft->effector_weights->group, IDWALK_CB_NOP); } } } @@ -1158,8 +1183,10 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL; pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) { /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ - if (pid_src == NULL || pid_dst->type != pid_src->type || - pid_dst->file_type != pid_src->file_type || + if (pid_src == NULL) { + continue; + } + if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { @@ -1190,6 +1217,40 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src) BLI_freelistN(&pidlist_src); } +static IDProperty *object_asset_dimensions_property(Object *ob) +{ + float dimensions[3]; + BKE_object_dimensions_get(ob, dimensions); + if (is_zero_v3(dimensions)) { + return NULL; + } + + IDPropertyTemplate idprop = {0}; + idprop.array.len = ARRAY_SIZE(dimensions); + idprop.array.type = IDP_FLOAT; + + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions"); + memcpy(IDP_Array(property), dimensions, sizeof(dimensions)); + + return property; +} + +static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) +{ + Object *ob = asset_ptr; + BLI_assert(GS(ob->id.name) == ID_OB); + + /* Update dimensions hint for the asset. */ + IDProperty *dimensions_prop = object_asset_dimensions_property(ob); + if (dimensions_prop) { + BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop); + } +} + +AssetTypeInfo AssetType_OB = { + .pre_save_fn = object_asset_pre_save, +}; + IDTypeInfo IDType_ID_OB = { .id_code = ID_OB, .id_filter = FILTER_ID_OB, @@ -1216,6 +1277,8 @@ IDTypeInfo IDType_ID_OB = { .blend_read_undo_preserve = NULL, .lib_override_apply_post = object_lib_override_apply_post, + + .asset_type_info = &AssetType_OB, }; void BKE_object_workob_clear(Object *workob) @@ -2815,7 +2878,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); #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 7b2a1af7086..5b62761bd91 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -173,28 +173,29 @@ static void particle_settings_free_data(ID *id) static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) { ParticleSettings *psett = (ParticleSettings *)id; - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_collection, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->bb_ob, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->bb_ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->collision_group, IDWALK_CB_NOP); 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])); } } if (psett->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, psett->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_NOP); } if (psett->pd) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->f_source, IDWALK_CB_NOP); } if (psett->pd2) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->f_source, IDWALK_CB_NOP); } if (psett->boids) { @@ -202,18 +203,18 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; - BKE_LIB_FOREACHID_PROCESS(data, gabr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gabr->ob, IDWALK_CB_NOP); } else if (rule->type == eBoidRuleType_FollowLeader) { BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; - BKE_LIB_FOREACHID_PROCESS(data, flbr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, flbr->ob, IDWALK_CB_NOP); } } } } LISTBASE_FOREACH (ParticleDupliWeight *, dw, &psett->instance_weights) { - BKE_LIB_FOREACHID_PROCESS(data, dw->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, dw->ob, IDWALK_CB_NOP); } } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 1db14dc3dc8..15c5a809118 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -105,7 +105,7 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data) { PointCloud *pointcloud = (PointCloud *)id; for (int i = 0; i < pointcloud->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, pointcloud->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pointcloud->mat[i], IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index 0b8e8d7c311..79a8b591f72 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -120,7 +120,8 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef) return; } - bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL); + bUserAssetLibrary *library = BKE_preferences_asset_library_add( + userdef, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME, NULL); /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ BLI_path_join( diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 2cb0213a192..a827e1c32a2 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,7 +534,21 @@ static void scene_foreach_toolsettings_id_pointer_process( (ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \ } \ else { \ - BKE_LIB_FOREACHID_PROCESS(__data, __id, __cb_flag); \ + 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 @@ -541,13 +559,13 @@ static void scene_foreach_paint(LibraryForeachIDData *data, 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) { @@ -695,7 +755,7 @@ static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase (lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP; - BKE_LIB_FOREACHID_PROCESS(data, lc->collection, cb_flag); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lc->collection, cb_flag); scene_foreach_layer_collection(data, &lc->layer_collections); } } @@ -704,32 +764,33 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; -#define FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#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; \ } \ } \ ((void)0) - FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF); - FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP); - FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->mask, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->sound, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->scene, IDWALK_CB_NEVER_SELF); + FOREACHID_PROCESS_IDSUPER(data, seq->scene_camera, IDWALK_CB_NOP); + FOREACHID_PROCESS_IDSUPER(data, seq->clip, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->mask, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->sound, IDWALK_CB_USER); IDP_foreach_property( seq->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { - FOREACHID_PROCESS(data, smd->mask_id, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER); } if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) { TextVars *text_data = seq->effectdata; - FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, text_data->text_font, IDWALK_CB_USER); } -#undef FOREACHID_PROCESS +#undef FOREACHID_PROCESS_IDSUPER return true; } @@ -738,66 +799,77 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) { Scene *scene = (Scene *)id; - BKE_LIB_FOREACHID_PROCESS(data, scene->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, scene->world, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->set, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, scene->clip, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->gpd, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->r.bake.cage_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->world, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->set, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->gpd, IDWALK_CB_USER); + 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) { - BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( 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) { - BKE_LIB_FOREACHID_PROCESS(data, fmc->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fmc->script, IDWALK_CB_NOP); } } LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { if (fls->group) { - BKE_LIB_FOREACHID_PROCESS(data, fls->group, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->group, IDWALK_CB_USER); } if (fls->linestyle) { - BKE_LIB_FOREACHID_PROCESS(data, fls->linestyle, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->linestyle, IDWALK_CB_USER); } } } LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { - BKE_LIB_FOREACHID_PROCESS(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_IDSUPER(data, marker->camera, IDWALK_CB_NOP); + 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); #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 0474c2b81cb..69e926caeae 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -93,13 +93,17 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet * { if (ads != NULL) { BKE_LIB_FOREACHID_PROCESS_ID(data, ads->source, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, ads->filter_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ads->filter_grp, IDWALK_CB_NOP); } } +/** + * 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(data, area->full, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP); /* TODO: this should be moved to a callback in `SpaceType`, defined in each editor's own code. * Will be for a later round of cleanup though... */ @@ -107,24 +111,21 @@ 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(data, v3d->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, v3d->ob_center, IDWALK_CB_NOP); - + 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(data, v3d->localvd->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } break; } 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,48 +133,41 @@ 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(data, saction->action, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP); break; } case SPACE_IMAGE: { SpaceImage *sima = (SpaceImage *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sima->image, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->gpd, IDWALK_CB_USER); + 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); break; } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sseq->gpd, IDWALK_CB_USER); + 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(data, st->text, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP); break; } case SPACE_SCRIPT: { SpaceScript *scpt = (SpaceScript *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, scpt->script, IDWALK_CB_NOP); + 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,26 +181,24 @@ 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( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ - BKE_LIB_FOREACHID_PROCESS(data, - path->nodetree, - is_private_nodetree ? IDWALK_CB_EMBEDDED : - IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, + path->nodetree, + is_private_nodetree ? IDWALK_CB_EMBEDDED : + IDWALK_CB_USER_ONE); } else { - BKE_LIB_FOREACHID_PROCESS(data, path->nodetree, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE); } if (path->nodetree == NULL) { @@ -214,22 +206,20 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } } - BKE_LIB_FOREACHID_PROCESS(data, snode->edittree, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP); break; } case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sclip->clip, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); + 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( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP); } } @@ -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/speaker.c b/source/blender/blenkernel/intern/speaker.c index b361f31cc30..230ff9d6da0 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -50,7 +50,7 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data) { Speaker *speaker = (Speaker *)id; - BKE_LIB_FOREACHID_PROCESS(data, speaker->sound, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, speaker->sound, IDWALK_CB_USER); } static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 663c1951ba3..bbe4e0aab7b 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -486,6 +486,12 @@ Array<float> Spline::sample_uniform_index_factors(const int samples_size) const prev_length = length; } + /* Zero lengths or float innacuracies can cause invalid values, or simply + * skip some, so set the values that weren't completed in the main loop. */ + for (const int i : IndexRange(i_sample, samples_size - i_sample)) { + samples[i] = float(samples_size); + } + if (!is_cyclic_) { /* In rare cases this can prevent overflow of the stored index. */ samples.last() = lengths.size(); diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index d5f7647f07a..0811e6cb675 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -142,9 +142,10 @@ 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(data, texture->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER); } static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -233,8 +234,8 @@ IDTypeInfo IDType_ID_TE = { /* Utils for all IDs using those texture slots. */ void BKE_texture_mtex_foreach_id(LibraryForeachIDData *data, MTex *mtex) { - BKE_LIB_FOREACHID_PROCESS(data, mtex->object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, mtex->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->tex, IDWALK_CB_USER); } /* ****************** Mapping ******************* */ diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 0b9ef5c537d..a72b5268e1d 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"}; using blender::float3; using blender::float4x4; using blender::IndexRange; +using blender::StringRef; #ifdef WITH_OPENVDB # include <atomic> @@ -556,7 +558,7 @@ static void volume_foreach_id(ID *id, LibraryForeachIDData *data) { Volume *volume = (Volume *)id; for (int i = 0; i < volume->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, volume->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, volume->mat[i], IDWALK_CB_USER); } } @@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType #endif } +#ifdef WITH_OPENVDB +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + const StringRef name, + openvdb::GridBase::Ptr vdb_grid) +{ + VolumeGridVector &grids = *volume.runtime.grids; + BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr); + BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN); + + vdb_grid->setName(name); + grids.emplace_back(vdb_grid); + return &grids.back(); +} +#endif + void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid) { #ifdef WITH_OPENVDB diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index e9d6eea4614..6e465b2fdf0 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -121,46 +121,66 @@ struct VolumeToMeshOp { } }; -static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts, - Span<openvdb::Vec3I> tris, - Span<openvdb::Vec4I> quads) +/** + * Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure. + * This can be used to add mesh data from a grid into an existing mesh rather than merging multiple + * meshes later on. + */ +void fill_mesh_from_openvdb_data(const Span<openvdb::Vec3s> vdb_verts, + const Span<openvdb::Vec3I> vdb_tris, + const Span<openvdb::Vec4I> vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan<MVert> verts, + MutableSpan<MPoly> polys, + MutableSpan<MLoop> loops) { - const int tot_loops = 3 * tris.size() + 4 * quads.size(); - const int tot_polys = tris.size() + quads.size(); - - Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys); - /* Write vertices. */ - for (const int i : verts.index_range()) { - const blender::float3 co = blender::float3(verts[i].asV()); - copy_v3_v3(mesh->mvert[i].co, co); + for (const int i : vdb_verts.index_range()) { + const blender::float3 co = blender::float3(vdb_verts[i].asV()); + copy_v3_v3(verts[vert_offset + i].co, co); } /* Write triangles. */ - for (const int i : tris.index_range()) { - mesh->mpoly[i].loopstart = 3 * i; - mesh->mpoly[i].totloop = 3; + for (const int i : vdb_tris.index_range()) { + polys[poly_offset + i].loopstart = loop_offset + 3 * i; + polys[poly_offset + i].totloop = 3; for (int j = 0; j < 3; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[3 * i + j].v = tris[i][2 - j]; + loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j]; } } /* Write quads. */ - const int poly_offset = tris.size(); - const int loop_offset = tris.size() * 3; - for (const int i : quads.index_range()) { - mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i; - mesh->mpoly[poly_offset + i].totloop = 4; + const int quad_offset = poly_offset + vdb_tris.size(); + const int quad_loop_offset = loop_offset + vdb_tris.size() * 3; + for (const int i : vdb_quads.index_range()) { + polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i; + polys[quad_offset + i].totloop = 4; for (int j = 0; j < 4; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j]; + loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j]; } } +} - BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); - return mesh; +/** + * Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and + * triangle indices. + */ +bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity) +{ + const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + + VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; + if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { + return {}; + } + return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)}; } Mesh *volume_to_mesh(const openvdb::GridBase &grid, @@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid, const float threshold, const float adaptivity) { - const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data( + grid, resolution, threshold, adaptivity); + + const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size(); + const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size(); + Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys); + + fill_mesh_from_openvdb_data(mesh_data.verts, + mesh_data.tris, + mesh_data.quads, + 0, + 0, + 0, + {mesh->mvert, mesh->totvert}, + {mesh->mpoly, mesh->totpoly}, + {mesh->mloop, mesh->totloop}); - VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; - if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { - return nullptr; - } + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); - return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); + return mesh; } #endif /* WITH_OPENVDB */ diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 3c168a6c7b2..6269cfc4349 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -82,7 +82,7 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) WorkSpace *workspace = (WorkSpace *)id; LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { - BKE_LIB_FOREACHID_PROCESS(data, layout->screen, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, 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 new file mode 100644 index 00000000000..7b8aa03b807 --- /dev/null +++ b/source/blender/blenlib/BLI_serialize.hh @@ -0,0 +1,329 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * An abstraction layer for serialization formats. + * + * Allowing to read/write data to a serialization format like JSON. + * + * + * + * # Supported data types + * + * The abstraction layer has a limited set of data types it supports. + * There are specific classes that builds up the data structure that + * can be (de)serialized. + * + * - StringValue: for strings + * - IntValue: for integer values + * - DoubleValue: for double precision floating point numbers + * - BooleanValue: for boolean values + * - ArrayValue: An array of any supported value. + * - ObjectValue: A key value pair where keys are std::string. + * - NullValue: for null values. + * + * # Basic usage + * + * ## Serializing + * + * - Construct a structure that needs to be serialized using the `*Value` classes. + * - Construct the formatter you want to use + * - Invoke the formatter.serialize method passing an output stream and the value. + * + * The next example would format an integer value (42) as JSON the result will + * be stored inside `out`. + * + * \code{.cc} + * JsonFormatter json; + * std::stringstream out; + * IntValue test_value(42); + * json.serialize(out, test_value); + * \endcode + * + * ## Deserializing + * + * \code{.cc} + * std::stringstream is("42"); + * JsonFormatter json; + * std::unique_ptr<Value> value = json.deserialize(is); + * \endcode + * + * # Adding a new formatter + * + * To add a new formatter a new sub-class of `Formatter` must be created and the + * `serialize`/`deserialize` methods should be implemented. + * + */ + +#include <ostream> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +namespace blender::io::serialize { + +/** + * Enumeration containing all sub-classes of Value. It is used as for type checking. + * + * \see #Value::type() + */ +enum class eValueType { + String, + Int, + Array, + Null, + Boolean, + Double, + Object, +}; + +class Value; +class StringValue; +class ObjectValue; +template<typename T, eValueType V> class PrimitiveValue; +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; +/* 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>; + +/** + * Class containing a (de)serializable value. + * + * To serialize from or to a specific format the Value will be used as an intermediate container + * holding the values. Value class is abstract. There are concrete classes to for different data + * types. + * + * - `StringValue`: contains a string. + * - `IntValue`: contains an integer. + * - `ArrayValue`: contains an array of elements. Elements don't need to be the same type. + * - `NullValue`: represents nothing (null pointer or optional). + * - `BooleanValue`: contains a boolean (true/false). + * - `DoubleValue`: contains a double precision floating point number. + * - `ObjectValue`: represents an object (key value pairs where keys are strings and values can be + * of different types. + * + */ +class Value { + private: + eValueType type_; + + protected: + Value() = delete; + explicit Value(eValueType type) : type_(type) + { + } + + public: + virtual ~Value() = default; + const eValueType type() const + { + return type_; + } + + /** + * Casts to a StringValue. + * Will return nullptr when it is a different type. + */ + const StringValue *as_string_value() const; + + /** + * Casts to an IntValue. + * Will return nullptr when it is a different type. + */ + const IntValue *as_int_value() const; + + /** + * Casts to a DoubleValue. + * Will return nullptr when it is a different type. + */ + const DoubleValue *as_double_value() const; + + /** + * Casts to a BooleanValue. + * Will return nullptr when it is a different type. + */ + const BooleanValue *as_boolean_value() const; + + /** + * Casts to an ArrayValue. + * Will return nullptr when it is a different type. + */ + const ArrayValue *as_array_value() const; + + /** + * Casts to an ObjectValue. + * Will return nullptr when it is a different type. + */ + const ObjectValue *as_object_value() const; +}; + +/** + * For generating value types that represent types that are typically known processor data types. + */ +template< + /** Wrapped c/cpp data type that is used to store the value. */ + typename T, + /** Value type of the class. */ + eValueType V> +class PrimitiveValue : public Value { + private: + T inner_value_{}; + + public: + explicit PrimitiveValue(const T value) : Value(V), inner_value_(value) + { + } + + const T value() const + { + return inner_value_; + } +}; + +class NullValue : public Value { + public: + NullValue() : Value(eValueType::Null) + { + } +}; + +class StringValue : public Value { + private: + std::string string_; + + public: + StringValue(const StringRef string) : Value(eValueType::String), string_(string) + { + } + + const std::string &value() const + { + return string_; + } +}; + +/** + * Template for arrays and objects. + * + * Both ArrayValue and ObjectValue store their values in an array. + */ +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> +class ContainerValue : public Value { + public: + using Items = Container; + using Item = ContainerItem; + + private: + Container inner_value_; + + public: + ContainerValue() : Value(V) + { + } + + const Container &elements() const + { + return inner_value_; + } + + Container &elements() + { + return inner_value_; + } +}; + +/** + * Internal storage type for ObjectValue. + * + * The elements are stored as an key value pair. The value is a shared pointer so it can be shared + * when using `ObjectValue::create_lookup`. + */ +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> { + public: + using LookupValue = std::shared_ptr<Value>; + using Lookup = Map<std::string, LookupValue>; + + /** + * Return a lookup map to quickly lookup by key. + * + * The lookup is owned by the caller. + */ + const Lookup create_lookup() const + { + Lookup result; + for (const Item &item : elements()) { + result.add_as(item.first, item.second); + } + return result; + } +}; + +/** + * Interface for any provided Formatter. + */ +class Formatter { + public: + virtual ~Formatter() = default; + + /** Serialize the value to the given stream. */ + virtual void serialize(std::ostream &os, const Value &value) = 0; + + /** Deserialize the stream. */ + virtual std::unique_ptr<Value> deserialize(std::istream &is) = 0; +}; + +/** + * Formatter to (de)serialize a JSON formatted stream. + */ +class JsonFormatter : public Formatter { + public: + /** + * The indentation level to use. + * Typically number of chars. Set to 0 to not use indentation. + */ + int8_t indentation_len = 0; + + public: + void serialize(std::ostream &os, const Value &value) override; + std::unique_ptr<Value> deserialize(std::istream &is) override; +}; + +} // namespace blender::io::serialize diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index c01052f0111..7db984aef5c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../../intern/guardedalloc ../../../intern/numaapi/include ../../../extern/wcwidth + ../../../extern/json/include ) set(INC_SYS @@ -126,6 +127,7 @@ set(SRC intern/scanfill.c intern/scanfill_utils.c intern/session_uuid.c + intern/serialize.cc intern/smallhash.c intern/sort.c intern/sort_utils.c @@ -282,6 +284,7 @@ set(SRC BLI_session_uuid.h BLI_set.hh BLI_set_slots.hh + BLI_serialize.hh BLI_simd.h BLI_smallhash.h BLI_sort.h @@ -364,6 +367,10 @@ if(WITH_GMP) endif() if(WIN32) + if (WITH_BLENDER_THUMBNAILER) + # Needed for querying the thumbnailer .dll in winstuff.c + add_definitions(-DWITH_BLENDER_THUMBNAILER) + endif() list(APPEND INC ../../../intern/utfconv ) @@ -448,6 +455,7 @@ if(WITH_GTESTS) tests/BLI_span_test.cc tests/BLI_stack_cxx_test.cc tests/BLI_stack_test.cc + tests/BLI_serialize_test.cc tests/BLI_string_ref_test.cc tests/BLI_string_search_test.cc tests/BLI_string_test.cc diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index 5fa2746d07f..4259237af6e 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -1528,9 +1528,15 @@ void voronoi_f1( targetPosition = pointPosition; } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_w = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_w != nullptr) { + *r_w = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float w, @@ -1555,14 +1561,26 @@ void voronoi_smooth_f1(const float w, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_w != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_w != nullptr) { + smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_w != nullptr) { + *r_w = cellPosition + smoothPosition; } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_w = cellPosition + smoothPosition; } void voronoi_f2( @@ -1596,9 +1614,15 @@ void voronoi_f2( positionF2 = pointPosition; } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_w = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_w != nullptr) { + *r_w = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance) @@ -1706,9 +1730,15 @@ void voronoi_f1(const float2 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float2 coord, @@ -1737,15 +1767,28 @@ void voronoi_smooth_f1(const float2 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float2 coord, @@ -1787,9 +1830,15 @@ void voronoi_f2(const float2 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance) @@ -1928,9 +1977,15 @@ void voronoi_f1(const float3 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float3 coord, @@ -1960,16 +2015,29 @@ void voronoi_smooth_f1(const float3 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float3 coord, @@ -2013,9 +2081,15 @@ void voronoi_f2(const float3 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance) @@ -2166,9 +2240,15 @@ void voronoi_f1(const float4 coord, } } } - *r_distance = minDistance; - *r_color = hash_float_to_float3(cellPosition + targetOffset); - *r_position = targetPosition + cellPosition; + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } } void voronoi_smooth_f1(const float4 coord, @@ -2200,18 +2280,30 @@ void voronoi_smooth_f1(const float4 coord, 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); float correctionFactor = smoothness * h * (1.0f - h); smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; - correctionFactor /= 1.0f + 3.0f * smoothness; - const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); - smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; - smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } } } } } - *r_distance = smoothDistance; - *r_color = smoothColor; - *r_position = cellPosition + smoothPosition; + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } } void voronoi_f2(const float4 coord, @@ -2258,9 +2350,15 @@ void voronoi_f2(const float4 coord, } } } - *r_distance = distanceF2; - *r_color = hash_float_to_float3(cellPosition + offsetF2); - *r_position = positionF2 + cellPosition; + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } } void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance) diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc new file mode 100644 index 00000000000..52aff140e3e --- /dev/null +++ b/source/blender/blenlib/intern/serialize.cc @@ -0,0 +1,216 @@ +#include "BLI_serialize.hh" + +#include "json.hpp" + +namespace blender::io::serialize { + +const StringValue *Value::as_string_value() const +{ + if (type_ != eValueType::String) { + return nullptr; + } + return static_cast<const StringValue *>(this); +} + +const IntValue *Value::as_int_value() const +{ + if (type_ != eValueType::Int) { + return nullptr; + } + return static_cast<const IntValue *>(this); +} + +const DoubleValue *Value::as_double_value() const +{ + if (type_ != eValueType::Double) { + return nullptr; + } + return static_cast<const DoubleValue *>(this); +} + +const BooleanValue *Value::as_boolean_value() const +{ + if (type_ != eValueType::Boolean) { + return nullptr; + } + return static_cast<const BooleanValue *>(this); +} + +const ArrayValue *Value::as_array_value() const +{ + if (type_ != eValueType::Array) { + return nullptr; + } + return static_cast<const ArrayValue *>(this); +} + +const ObjectValue *Value::as_object_value() const +{ + if (type_ != eValueType::Object) { + return nullptr; + } + return static_cast<const ObjectValue *>(this); +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value); +static void convert_to_json(nlohmann::ordered_json &j, const ArrayValue &value) +{ + const ArrayValue::Items &items = value.elements(); + /* Create a json array to store the elements. If this isn't done and items is empty it would + * return use a null value, in stead of an empty array. */ + j = "[]"_json; + for (const ArrayValue::Item &item_value : items) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *item_value); + j.push_back(json_item); + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const ObjectValue &value) +{ + const ObjectValue::Items &attributes = value.elements(); + /* Create a json object to store the attributes. If this isn't done and attributes is empty it + * would return use a null value, in stead of an empty object. */ + j = "{}"_json; + for (const ObjectValue::Item &attribute : attributes) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *attribute.second); + j[attribute.first] = json_item; + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value) +{ + switch (value.type()) { + case eValueType::String: { + j = value.as_string_value()->value(); + break; + } + + case eValueType::Int: { + j = value.as_int_value()->value(); + break; + } + + case eValueType::Array: { + const ArrayValue &array = *value.as_array_value(); + convert_to_json(j, array); + break; + } + + case eValueType::Object: { + const ObjectValue &object = *value.as_object_value(); + convert_to_json(j, object); + break; + } + + case eValueType::Null: { + j = nullptr; + break; + } + + case eValueType::Boolean: { + j = value.as_boolean_value()->value(); + break; + } + + case eValueType::Double: { + j = value.as_double_value()->value(); + } + } +} + +static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j); +static std::unique_ptr<ArrayValue> convert_from_json_to_array(const nlohmann::ordered_json &j) +{ + std::unique_ptr<ArrayValue> array = std::make_unique<ArrayValue>(); + ArrayValue::Items &elements = array->elements(); + for (auto element : j.items()) { + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr<Value> value = convert_from_json(element_json); + elements.append_as(value.release()); + } + return array; +} + +static std::unique_ptr<ObjectValue> convert_from_json_to_object(const nlohmann::ordered_json &j) +{ + std::unique_ptr<ObjectValue> object = std::make_unique<ObjectValue>(); + ObjectValue::Items &elements = object->elements(); + for (auto element : j.items()) { + std::string key = element.key(); + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr<Value> value = convert_from_json(element_json); + elements.append_as(std::pair(key, value.release())); + } + return object; +} + +static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j) +{ + switch (j.type()) { + case nlohmann::json::value_t::array: { + return convert_from_json_to_array(j); + } + + case nlohmann::json::value_t::object: { + return convert_from_json_to_object(j); + } + + case nlohmann::json::value_t::string: { + std::string value = j; + return std::make_unique<StringValue>(value); + } + + case nlohmann::json::value_t::null: { + return std::make_unique<NullValue>(); + } + + case nlohmann::json::value_t::boolean: { + return std::make_unique<BooleanValue>(j); + } + case nlohmann::json::value_t::number_integer: + case nlohmann::json::value_t::number_unsigned: { + return std::make_unique<IntValue>(j); + } + + case nlohmann::json::value_t::number_float: { + return std::make_unique<DoubleValue>(j); + } + + case nlohmann::json::value_t::binary: + case nlohmann::json::value_t::discarded: + /* + * Binary data isn't supported. + * Discarded is an internal type of nlohmann. + * + * Assert in case we need to parse them. + */ + BLI_assert_unreachable(); + return std::make_unique<NullValue>(); + } + + BLI_assert_unreachable(); + return std::make_unique<NullValue>(); +} + +void JsonFormatter::serialize(std::ostream &os, const Value &value) +{ + nlohmann::ordered_json j; + convert_to_json(j, value); + if (indentation_len) { + os << j.dump(indentation_len); + } + else { + os << j.dump(); + } +} + +std::unique_ptr<Value> JsonFormatter::deserialize(std::istream &is) +{ + nlohmann::ordered_json j; + is >> j; + return convert_from_json(j); +} + +} // namespace blender::io::serialize diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index d5c9c5cd5e6..3001b25bc1e 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -172,12 +172,14 @@ bool BLI_windows_register_blend_extension(const bool background) return false; } +# ifdef WITH_BLENDER_THUMBNAILER BLI_windows_get_executable_dir(InstallDir); GetSystemDirectory(SysDir, FILE_MAXDIR); ThumbHandlerDLL = "BlendThumb.dll"; snprintf( RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL); system(RegCmd); +# endif RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); diff --git a/source/blender/blenlib/tests/BLI_serialize_test.cc b/source/blender/blenlib/tests/BLI_serialize_test.cc new file mode 100644 index 00000000000..6c55a85ca1e --- /dev/null +++ b/source/blender/blenlib/tests/BLI_serialize_test.cc @@ -0,0 +1,207 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_serialize.hh" + +/* -------------------------------------------------------------------- */ +/* tests */ + +namespace blender::io::serialize::json::testing { + +TEST(serialize, string_to_json) +{ + JsonFormatter json; + std::stringstream out; + StringValue test_value("Hello JSON"); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "\"Hello JSON\""); +} + +static void test_int_to_json(int64_t value, StringRef expected) +{ + JsonFormatter json; + std::stringstream out; + IntValue test_value(value); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), expected); +} + +TEST(serialize, int_to_json) +{ + test_int_to_json(42, "42"); + test_int_to_json(-42, "-42"); + test_int_to_json(std::numeric_limits<int32_t>::max(), "2147483647"); + test_int_to_json(std::numeric_limits<int32_t>::min(), "-2147483648"); + test_int_to_json(std::numeric_limits<int64_t>::max(), "9223372036854775807"); + test_int_to_json(std::numeric_limits<int64_t>::min(), "-9223372036854775808"); +} + +TEST(serialize, double_to_json) +{ + JsonFormatter json; + std::stringstream out; + DoubleValue test_value(42.31); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "42.31"); +} + +TEST(serialize, null_to_json) +{ + JsonFormatter json; + std::stringstream out; + NullValue test_value; + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "null"); +} + +TEST(serialize, false_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(false); + json.serialize(out, value); + EXPECT_EQ(out.str(), "false"); +} + +TEST(serialize, true_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(true); + json.serialize(out, value); + EXPECT_EQ(out.str(), "true"); +} + +TEST(serialize, array_to_json) +{ + JsonFormatter json; + std::stringstream out; + ArrayValue value_array; + ArrayValue::Items &array = value_array.elements(); + array.append_as(new IntValue(42)); + array.append_as(new StringValue("Hello JSON")); + array.append_as(new NullValue); + array.append_as(new BooleanValue(false)); + array.append_as(new BooleanValue(true)); + + json.serialize(out, value_array); + EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]"); +} + +TEST(serialize, object_to_json) +{ + JsonFormatter json; + std::stringstream out; + ObjectValue value_object; + ObjectValue::Items &attributes = value_object.elements(); + attributes.append_as(std::pair(std::string("best_number"), new IntValue(42))); + + json.serialize(out, value_object); + EXPECT_EQ(out.str(), "{\"best_number\":42}"); +} + +TEST(serialize, json_roundtrip_ordering) +{ + const std::string input = + "[{\"_id\":\"614ada7c476c472ecbd0ecbb\",\"index\":0,\"guid\":\"d5b81381-cef8-4327-923d-" + "41e57ff79326\",\"isActive\":false,\"balance\":\"$2,062.25\",\"picture\":\"http://" + "placehold.it/32x32\",\"age\":26,\"eyeColor\":\"brown\",\"name\":\"Geneva " + "Vega\",\"gender\":\"female\",\"company\":\"SLOGANAUT\",\"email\":\"genevavega@sloganaut." + "com\",\"phone\":\"+1 (993) 432-2805\",\"address\":\"943 Christopher Avenue, Northchase, " + "Alabama, 5769\",\"about\":\"Eu cillum qui eu fugiat sit nulla eu duis. Aliqua nulla aliqua " + "ea tempor dolor fugiat sint consectetur exercitation ipsum magna ex. Aute laborum esse " + "magna nostrud in cillum et mollit proident. Deserunt ex minim adipisicing incididunt " + "incididunt dolore velit aliqua.\\r\\n\",\"registered\":\"2014-06-02T06:29:33 " + "-02:00\",\"latitude\":-66.003108,\"longitude\":44.038986,\"tags\":[\"exercitation\"," + "\"laborum\",\"velit\",\"magna\",\"officia\",\"aliqua\",\"laboris\"],\"friends\":[{\"id\":0," + "\"name\":\"Daniel Stuart\"},{\"id\":1,\"name\":\"Jackson " + "Velez\"},{\"id\":2,\"name\":\"Browning Boyd\"}],\"greeting\":\"Hello, Geneva Vega! You " + "have 8 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7cf28685063c6722af\"," + "\"index\":1,\"guid\":\"e157edf3-a86d-4984-b18d-e2fe568a9915\",\"isActive\":false," + "\"balance\":\"$3,550.44\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":40,\"eyeColor\":\"blue\",\"name\":\"Lamb " + "Lowe\",\"gender\":\"male\",\"company\":\"PROXSOFT\",\"email\":\"lamblowe@proxsoft.com\"," + "\"phone\":\"+1 (999) 573-2855\",\"address\":\"632 Rockwell Place, Diaperville, " + "Pennsylvania, 5050\",\"about\":\"Anim dolor deserunt esse quis velit adipisicing aute " + "nostrud velit minim culpa aute et tempor. Dolor aliqua reprehenderit anim voluptate. " + "Consequat proident ut culpa reprehenderit qui. Nisi proident velit cillum voluptate. " + "Ullamco id sunt quis aute adipisicing cupidatat consequat " + "aliquip.\\r\\n\",\"registered\":\"2014-09-06T06:13:36 " + "-02:00\",\"latitude\":-44.550228,\"longitude\":-80.893356,\"tags\":[\"anim\",\"id\"," + "\"irure\",\"do\",\"officia\",\"irure\",\"Lorem\"],\"friends\":[{\"id\":0,\"name\":" + "\"Faulkner Watkins\"},{\"id\":1,\"name\":\"Cecile Schneider\"},{\"id\":2,\"name\":\"Burt " + "Lester\"}],\"greeting\":\"Hello, Lamb Lowe! You have 1 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c235335fc56bc2f78\"," + "\"index\":2,\"guid\":\"8206bad1-8274-49fd-9223-d727589f22ca\",\"isActive\":false," + "\"balance\":\"$2,548.34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":37,\"eyeColor\":\"blue\",\"name\":\"Sallie " + "Chase\",\"gender\":\"female\",\"company\":\"FLEETMIX\",\"email\":\"salliechase@fleetmix." + "com\",\"phone\":\"+1 (953) 453-3388\",\"address\":\"865 Irving Place, Chelsea, Utah, " + "9777\",\"about\":\"In magna exercitation incididunt exercitation dolor anim. Consectetur " + "dolore commodo elit cillum dolor reprehenderit magna minim et ex labore pariatur. Nulla " + "ullamco officia velit in aute proident nostrud. Duis deserunt et labore Lorem aliqua " + "eiusmod commodo sunt.\\r\\n\",\"registered\":\"2017-03-16T08:54:53 " + "-01:00\",\"latitude\":-78.481939,\"longitude\":-149.820215,\"tags\":[\"Lorem\",\"ipsum\"," + "\"in\",\"tempor\",\"consectetur\",\"voluptate\",\"elit\"],\"friends\":[{\"id\":0,\"name\":" + "\"Gibson Garner\"},{\"id\":1,\"name\":\"Anna Frank\"},{\"id\":2,\"name\":\"Roberson " + "Daugherty\"}],\"greeting\":\"Hello, Sallie Chase! You have 7 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c93b63ecad5f9ba5e\",\"index\":3," + "\"guid\":\"924b02fc-7c27-481a-9941-db3b9403dfe1\",\"isActive\":true,\"balance\":\"$1,633." + "60\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":29,\"eyeColor\":\"brown\",\"name\":\"Grace " + "Mccall\",\"gender\":\"female\",\"company\":\"PIVITOL\",\"email\":\"gracemccall@pivitol." + "com\",\"phone\":\"+1 (964) 541-2514\",\"address\":\"734 Schaefer Street, Topaz, Virginia, " + "9137\",\"about\":\"Amet officia magna fugiat ut pariatur fugiat elit culpa voluptate elit " + "do proident culpa minim. Commodo do minim reprehenderit ut voluptate ut velit id esse " + "consequat. Labore ullamco deserunt irure eiusmod cillum tempor incididunt qui adipisicing " + "nostrud pariatur enim aliquip. Excepteur nostrud commodo consectetur esse duis irure " + "qui.\\r\\n\",\"registered\":\"2015-04-24T03:55:17 " + "-02:00\",\"latitude\":58.801446,\"longitude\":-157.413865,\"tags\":[\"do\",\"ea\",\"eu\"," + "\"eu\",\"qui\",\"duis\",\"sint\"],\"friends\":[{\"id\":0,\"name\":\"Carrie " + "Short\"},{\"id\":1,\"name\":\"Dickerson Barnes\"},{\"id\":2,\"name\":\"Rae " + "Rios\"}],\"greeting\":\"Hello, Grace Mccall! You have 5 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c9caf1353b0e22bbf\",\"index\":4," + "\"guid\":\"e5981ae1-90e4-41c4-9905-161522db700b\",\"isActive\":false,\"balance\":\"$3,660." + "34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"blue\",\"name\":\"Herring " + "Powers\",\"gender\":\"male\",\"company\":\"PYRAMIA\",\"email\":\"herringpowers@pyramia." + "com\",\"phone\":\"+1 (981) 541-2829\",\"address\":\"409 Furman Avenue, Waterloo, South " + "Carolina, 380\",\"about\":\"In officia culpa aliqua culpa pariatur aliqua mollit ex. Velit " + "est Lorem enim magna cillum sunt elit consectetur deserunt ea est consectetur fugiat " + "mollit. Aute Lorem excepteur minim esse qui. Id Lorem in tempor et. Nisi aliquip laborum " + "magna eu aute.\\r\\n\",\"registered\":\"2018-07-05T07:28:54 " + "-02:00\",\"latitude\":51.497405,\"longitude\":-129.422711,\"tags\":[\"eiusmod\",\"et\"," + "\"nostrud\",\"reprehenderit\",\"Lorem\",\"cillum\",\"nulla\"],\"friends\":[{\"id\":0," + "\"name\":\"Tonia Keith\"},{\"id\":1,\"name\":\"Leanne Rice\"},{\"id\":2,\"name\":\"Craig " + "Gregory\"}],\"greeting\":\"Hello, Herring Powers! You have 6 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c53a3d6da77468f25\"," + "\"index\":5,\"guid\":\"abb2eec9-c4f0-4a0d-b20a-5c8e50fe88a1\",\"isActive\":true," + "\"balance\":\"$1,481.08\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"green\",\"name\":\"Lela " + "Dillard\",\"gender\":\"female\",\"company\":\"CEMENTION\",\"email\":\"leladillard@" + "cemention.com\",\"phone\":\"+1 (856) 456-3657\",\"address\":\"391 Diamond Street, Madaket, " + "Ohio, 9337\",\"about\":\"Tempor dolor ullamco esse cillum excepteur. Excepteur aliqua non " + "enim anim esse amet cupidatat non. Cillum excepteur occaecat cupidatat elit labore. " + "Pariatur ut esse sint elit. Velit sint magna et commodo sit velit labore consectetur irure " + "officia proident aliquip. Aliqua dolore ipsum voluptate veniam deserunt amet irure. Cillum " + "consequat veniam proident Lorem in anim enim veniam ea " + "nulla.\\r\\n\",\"registered\":\"2017-01-11T11:07:22 " + "-01:00\",\"latitude\":86.349081,\"longitude\":-179.983754,\"tags\":[\"consequat\"," + "\"labore\",\"consectetur\",\"dolor\",\"laborum\",\"eiusmod\",\"in\"],\"friends\":[{\"id\":" + "0,\"name\":\"Hancock Rivera\"},{\"id\":1,\"name\":\"Chasity " + "Oneil\"},{\"id\":2,\"name\":\"Whitaker Barr\"}],\"greeting\":\"Hello, Lela Dillard! You " + "have 3 unread messages.\",\"favoriteFruit\":\"strawberry\"}]"; + std::stringstream is(input); + + JsonFormatter json; + std::unique_ptr<Value> value = json.deserialize(is); + EXPECT_EQ(value->type(), eValueType::Array); + + std::stringstream out; + json.serialize(out, *value); + EXPECT_EQ(out.str(), input); +} + +} // namespace blender::io::serialize::json::testing diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 0b69395b4f8..600abcca818 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2211,6 +2211,9 @@ static void direct_link_id_common( if (id->asset_data) { BLO_read_data_address(reader, &id->asset_data); BKE_asset_metadata_read(reader, id->asset_data); + /* Restore runtime asset type info. */ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + id->asset_data->local_type_info = id_type->asset_type_info; } /* Link direct data of ID properties. */ diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index e6247750759..e809d580fd2 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1717,18 +1717,8 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Paint Brush. This ensure that the brush paints by default. Used during the development and * patch review of the initial Sculpt Vertex Colors implementation (D5975) */ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { @@ -1748,6 +1738,20 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } /* NOTE: This version patch is intended for versions < 2.52.2, @@ -5066,17 +5070,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #do_versions_after_linking_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Set the cloth wind factor to 1 for old forces. */ if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { @@ -5096,10 +5091,22 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { /* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be - * edited and this flag should always be set. So we can always execute this. */ + * edited and this flag should always be set. */ wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #do_versions_after_linking_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d2c722f8be7..def14768ec6 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1985,7 +1985,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density"); } } FOREACH_NODETREE_END; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 8168b917b5e..68faa4c0672 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -535,7 +535,7 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) } /* Also realize instances for the profile input of the curve to mesh node. */ if (node->type == GEO_NODE_CURVE_TO_MESH) { - bNodeSocket *profile_socket = node->inputs.last; + bNodeSocket *profile_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); add_realize_instances_before_socket(ntree, node, profile_socket); } } @@ -967,7 +967,7 @@ static bool geometry_node_is_293_legacy(const short node_type) /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to * generate a mesh for all grids in the volume. */ - case GEO_NODE_VOLUME_TO_MESH: + case GEO_NODE_LEGACY_VOLUME_TO_MESH: return false; /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ @@ -1218,6 +1218,56 @@ static void do_version_bones_roll(ListBase *lb) } } +static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree) +{ + /* Add the new Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + if (BLI_listbase_count(&node->inputs) < 4) { + /* The offset socket didn't exist in the file yet. */ + return; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + if (old_offset_socket->type == SOCK_VECTOR) { + /* Versioning happened already. */ + return; + } + /* Change identifier of old socket, so that the there is no name collision. */ + STRNCPY(old_offset_socket->identifier, "Offset_old"); + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_VECTOR, PROP_TRANSLATION, "Offset", "Offset"); + } + + /* Relink links that were connected to Position while Offset was enabled. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tonode->type != GEO_NODE_SET_POSITION) { + continue; + } + if (!STREQ(link->tosock->identifier, "Position")) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3); + /* This assumes that the offset is not linked to something else. That seems to be a reasonable + * assumption, because the node is probably only ever used in one or the other mode. */ + const bool offset_enabled = + ((bNodeSocketValueBoolean *)old_offset_socket->default_value)->value; + if (offset_enabled) { + /* Relink to new offset socket. */ + link->tosock = old_offset_socket->next; + } + } + + /* Remove old Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + nodeRemoveSocket(ntree, node, old_offset_socket); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -2021,6 +2071,79 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { + LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { + wm->xr.session_settings.base_scale = 1.0f; + wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION | + V3D_OFSDRAW_XR_SHOW_CONTROLLERS | + V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS); + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 40)) { + /* Update the `idnames` for renamed geometry and function nodes. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); + version_geometry_nodes_set_position_node_offset(ntree); + version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); + } + + /* Add storage to viewer node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_VIEWER) { + if (node->storage == NULL) { + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN( + sizeof(NodeGeometryViewer), __func__); + data->data_type = CD_PROP_FLOAT; + node->storage = data; + } + } + } + } + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points"); + version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_RESAMPLE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_RADIUS, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_TILT, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_HANDLES, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_TRANSLATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_ROTATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_SCALE_INSTANCES, "Geometry", "Instances"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 1", "Mesh 1"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 2", "Mesh 2"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_MESH, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_TRIANGULATE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CONE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CUBE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_LINE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_SET_POINT_RADIUS, "Geometry", "Points"); + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -2030,15 +2153,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) * * \note Keep this message at the bottom of the function. */ - { - /* Update the `idnames` for renamed geometry and function nodes. */ - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type != NTREE_GEOMETRY) { - continue; - } - version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); - } /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 6c4996ba9b2..ecc944defba 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -87,6 +87,18 @@ ID *do_versions_rename_id(Main *bmain, return id; } +static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, sockets) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } +} + void version_node_socket_name(bNodeTree *ntree, const int node_type, const char *old_name, @@ -94,22 +106,32 @@ void version_node_socket_name(bNodeTree *ntree, { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } - } + change_node_socket_name(&node->inputs, old_name, new_name); + change_node_socket_name(&node->outputs, old_name, new_name); + } + } +} + +void version_node_input_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->inputs, old_name, new_name); + } + } +} + +void version_node_output_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->outputs, old_name, new_name); } } } diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 1826182be21..8697e8e2639 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -43,6 +43,14 @@ void version_node_socket_name(struct bNodeTree *ntree, const int node_type, const char *old_name, const char *new_name); +void version_node_input_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); +void version_node_output_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name); diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 170e6be715a..4234570af6c 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -27,9 +27,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#ifdef WITH_INTERNATIONAL -# include "BLT_translation.h" -#endif +#include "BLT_translation.h" #include "DNA_anim_types.h" #include "DNA_collection_types.h" @@ -60,10 +58,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) { #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver) - if (!USER_VERSION_ATLEAST(280, 20)) { - memcpy(btheme, &U_theme_default, sizeof(*btheme)); - } - #define FROM_DEFAULT_V4_UCHAR(member) copy_v4_v4_uchar(btheme->member, U_theme_default.member) if (!USER_VERSION_ATLEAST(280, 25)) { @@ -314,6 +308,15 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_node.dash_alpha = 0.5f; } + if (!USER_VERSION_ATLEAST(300, 39)) { + FROM_DEFAULT_V4_UCHAR(space_node.grid); + btheme->space_node.grid_levels = 7; + } + + if (!USER_VERSION_ATLEAST(300, 40)) { + memcpy(btheme, &U_theme_default, sizeof(*btheme)); + } + /** * Versioning code until next subversion bump goes here. * @@ -923,6 +926,24 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->dupflag |= USER_DUP_SPEAKER; } + if (!USER_VERSION_ATLEAST(300, 40)) { + /* Rename the default asset library from "Default" to "User Library" */ + LISTBASE_FOREACH (bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { + if (STREQ(asset_library->name, DATA_("Default"))) { + BKE_preferences_asset_library_name_set( + userdef, asset_library, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME); + } + } + } + + if (!USER_VERSION_ATLEAST(300, 40)) { + LISTBASE_FOREACH (uiStyle *, style, &userdef->uistyles) { + const int default_title_points = 11; /* UI_DEFAULT_TITLE_POINTS */ + style->paneltitle.points = default_title_points; + style->grouplabel.points = default_title_points; + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index baf83234354..0baf994d978 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -61,6 +61,7 @@ set(SRC intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc + intern/mesh_extractors/extract_mesh_vbo_attributes.cc intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc intern/mesh_extractors/extract_mesh_vbo_edit_data.cc intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b07e86000fd..7f9e37f58d5 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -463,9 +463,14 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) case OB_LIGHTPROBE: OVERLAY_lightprobe_cache_populate(vedata, ob); break; - case OB_LATTICE: - OVERLAY_lattice_cache_populate(vedata, ob); + case OB_LATTICE: { + /* Unlike the other types above, lattices actually have a bounding box defined, so hide the + * lattice wires if only the boundingbox is requested. */ + if (ob->dt > OB_BOUNDBOX) { + OVERLAY_lattice_cache_populate(vedata, ob); + } break; + } } } diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 31c8ed9d664..4a551c4dec5 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -200,6 +200,15 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) shd->grid_distance = dist / 2.0f; ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps); + + if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) { + /* The calculations for the grid parameters assume that the view matrix has no scale component, + * which may not be correct if the user is "shrunk" or "enlarged" by zooming in or out. + * Therefore, we need to compensate the values here. */ + float viewinvscale = len_v3( + viewinv[0]); /* Assumption is uniform scaling (all column vectors are of same length). */ + shd->grid_distance *= viewinvscale; + } } void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a680cc0d6b7..ba42cdf66e7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "DNA_customdata_types.h" + +#include "BKE_attribute.h" + #include "GPU_batch.h" #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" @@ -56,7 +60,6 @@ typedef struct DRW_MeshCDMask { uint32_t uv : 8; uint32_t tan : 8; uint32_t vcol : 8; - uint32_t sculpt_vcol : 8; uint32_t orco : 1; uint32_t tan_orco : 1; uint32_t sculpt_overlays : 1; @@ -64,10 +67,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; -/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. +/* Keep `DRW_MeshCDMask` struct within an `uint32_t`. * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ -BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, @@ -76,6 +79,17 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; +} DRW_AttributeRequest; + +typedef struct DRW_MeshAttributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_MeshAttributes; + typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -133,6 +147,7 @@ typedef struct MeshBufferList { GPUVertBuf *edge_idx; /* extend */ GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; + GPUVertBuf *attr[GPU_MAX_ATTR]; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -285,6 +300,8 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; + DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + int lastmatch; /* Valid only if edge_detection is up to date. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 06c449fe590..f3b72503907 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -650,6 +650,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vert_idx); EXTRACT_ADD_REQUESTED(vbo, fdot_idx); EXTRACT_ADD_REQUESTED(vbo, skin_roots); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + EXTRACT_ADD_REQUESTED(vbo, attr[i]); + } EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 18664498d00..12c19c671ab 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -41,6 +41,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -121,6 +122,8 @@ # define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) +# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20) +# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21); # define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) # define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) @@ -192,6 +195,21 @@ static const DRWBatchFlag g_buffer_deps[] = { [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), + [BUFFER_INDEX(vbo.attr) + 0] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 1] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 2] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 3] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 4] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 5] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 6] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 7] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 8] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 9] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 10] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 11] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 12] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 13] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 14] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, surface_weights, @@ -240,12 +258,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); + return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint64_t *)&a) == *((uint64_t *)&b); + return *((uint32_t *)&a) == *((uint32_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) @@ -253,12 +271,11 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) uint32_t *a_p = (uint32_t *)a; uint32_t *b_p = (uint32_t *)&b; atomic_fetch_and_or_uint32(a_p, *b_p); - atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint64_t *)a) = 0; + *((uint32_t *)a) = 0; } BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) @@ -271,6 +288,95 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } +/** \name DRW_MeshAttributes + * + * Utilities for handling requested attributes. + * \{ */ + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, + DRW_MeshAttributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_MeshAttributes)); +} + +static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, + const DRW_MeshAttributes *src, + ThreadMutex *mesh_render_mutex) +{ + BLI_mutex_lock(mesh_render_mutex); + mesh_attrs_merge_requests(src, dst); + BLI_mutex_unlock(mesh_render_mutex); +} + +/* Return true if all requests in b are in a. */ +static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) +{ + if (a->num_requests != b->num_requests) { + return false; + } + + for (int i = 0; i < a->num_requests; i++) { + if (!has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; +} + +/** \} */ + BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -286,6 +392,36 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) return &me->ldata; } +BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->pdata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->pdata; + break; + } + + BLI_assert(0); + return &me->pdata; +} + +BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->edata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->edata; + break; + } + + BLI_assert(0); + return &me->edata; +} + BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -321,14 +457,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); if (layer != -1) { - cd_used->sculpt_vcol |= (1 << layer); + drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); } } @@ -343,13 +479,45 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c } } +static bool custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[6] = { + CD_PROP_BOOL, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} + static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, struct GPUMaterial **gpumat_array, - int gpumat_array_len) + int gpumat_array_len, + DRW_MeshAttributes *attributes) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); + const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); + const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -363,6 +531,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, const char *name = gpu_attr->name; int type = gpu_attr->type; int layer = -1; + /* ATTR_DOMAIN_NUM is standard for "invalid value". */ + AttributeDomain domain = ATTR_DOMAIN_NUM; if (type == CD_AUTO_FROM_NAME) { /* We need to deduct what exact layer is used. @@ -374,13 +544,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, type = CD_MTFACE; if (layer == -1) { - if (U.experimental.use_sculpt_vertex_colors) { - layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); - type = CD_PROP_COLOR; - } - } - - if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); type = CD_MCOL; } @@ -392,6 +555,27 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } #endif if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; + domain = ATTR_DOMAIN_NUM; + } + } + + if (layer == -1) { continue; } } @@ -432,31 +616,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } break; } - case CD_PROP_COLOR: { - /* Sculpt Vertex Colors */ - bool use_mloop_cols = false; - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) : - CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR); - /* Fallback to Vertex Color data */ - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); - use_mloop_cols = true; - } - } - if (layer != -1) { - if (use_mloop_cols) { - cd_used.vcol |= (1 << layer); - } - else { - cd_used.sculpt_vcol |= (1 << layer); - } - } - break; - } case CD_MCOL: { /* Vertex Color Data */ if (layer == -1) { @@ -473,6 +632,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, cd_used.orco = 1; break; } + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } + break; + } } } } @@ -935,14 +1105,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) { - DRW_MeshCDMask cd_needed; - mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_vcol_layer(me, &cd_needed); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + mesh_cd_calc_active_vcol_layer(me, &attrs_needed); - BLI_assert(cd_needed.sculpt_vcol != 0 && + BLI_assert(attrs_needed.num_requests != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); - mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1015,11 +1185,16 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( + me, gpumat_array, gpumat_array_len, &attrs_needed); BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1296,11 +1471,25 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } + if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + cache->lastmatch = ctime; + } + if (ctime - cache->lastmatch > U.vbotimeout) { mesh_batch_cache_discard_shaded_tri(cache); } mesh_cd_layers_type_clear(&cache->cd_used_over_time); + drw_mesh_attributes_clear(&cache->attr_used_over_time); +} + +static void drw_add_attributes_vbo(GPUBatch *batch, + MeshBufferList *mbuflist, + DRW_MeshAttributes *attr_used) +{ + for (int i = 0; i < attr_used->num_requests; i++) { + DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); + } } #ifdef DEBUG @@ -1409,12 +1598,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - if (cd_overlap == false) { + bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); @@ -1430,11 +1622,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } - if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || - ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != - cache->cd_needed.sculpt_vcol)) { + if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } + if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); + } + } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ @@ -1445,9 +1640,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); + drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); + + drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_mesh_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { @@ -1506,7 +1705,27 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); + MDEPS_ASSERT(surface, + ibo.tris, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.vcol, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1515,9 +1734,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.uv != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } + drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used); } MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { @@ -1580,8 +1800,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ - MDEPS_ASSERT_FLAG( - SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.tan, + vbo.vcol, + vbo.orco, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { @@ -1595,12 +1835,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } + drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used); } } @@ -1751,6 +1992,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle); MDEPS_ASSERT_MAP(vbo.fdots_uv); MDEPS_ASSERT_MAP(vbo.fdots_edituv_data); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + MDEPS_ASSERT_MAP(vbo.attr[i]); + } MDEPS_ASSERT_MAP(ibo.tris); MDEPS_ASSERT_MAP(ibo.lines); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index f96bd474aec..5d3e3db866f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1684,10 +1684,12 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox, bsphere->center[0] = farcenter[0] * z / e; bsphere->center[1] = farcenter[1] * z / e; bsphere->center[2] = z; - bsphere->radius = len_v3v3(bsphere->center, farpoint); - /* Transform to world space. */ - mul_m4_v3(viewinv, bsphere->center); + /* For XR, the view matrix may contain a scale factor. Then, transforming only the center + * into world space after calculating the radius will result in incorrect behavior. */ + mul_m4_v3(viewinv, bsphere->center); /* Transform to world space. */ + mul_m4_v3(viewinv, farpoint); + bsphere->radius = len_v3v3(bsphere->center, farpoint); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d9f397fd8b8..d1ffef4fe92 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -328,6 +328,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; +extern const MeshExtract extract_attr[GPU_MAX_ATTR]; #ifdef __cplusplus } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc new file mode 100644 index 00000000000..9edefe32fbc --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -0,0 +1,398 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "MEM_guardedalloc.h" + +#include <functional> + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_string.h" + +#include "BKE_attribute.h" + +#include "extract_mesh.h" + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Extract Attributes + * \{ */ + +static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain) +{ + switch (domain) { + default: { + return nullptr; + } + case ATTR_DOMAIN_POINT: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + } + case ATTR_DOMAIN_CORNER: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + } + case ATTR_DOMAIN_FACE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + } + case ATTR_DOMAIN_EDGE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; + } + } +} + +/* Utility to convert from the type used in the attributes to the types for the VBO. + * This is mostly used to promote integers and booleans to floats, as other types (float, float2, + * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4 + * in the shader. + */ +template<typename AttributeType, typename VBOType> struct attribute_type_converter { + static VBOType convert_value(AttributeType value) + { + if constexpr (std::is_same_v<AttributeType, VBOType>) { + return value; + } + + /* This should only concern bools which are converted to floats. */ + return static_cast<VBOType>(value); + } +}; + +/* Similar to the one in #extract_mesh_vcol_vbo.cc */ +struct gpuMeshCol { + ushort r, g, b, a; +}; + +template<> struct attribute_type_converter<MPropCol, gpuMeshCol> { + static gpuMeshCol convert_value(MPropCol value) + { + gpuMeshCol result; + result.r = unit_float_to_ushort_clamp(value.color[0]); + result.g = unit_float_to_ushort_clamp(value.color[1]); + result.b = unit_float_to_ushort_clamp(value.color[2]); + result.a = unit_float_to_ushort_clamp(value.color[3]); + return result; + } +}; + +/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */ +static uint gpu_component_size_for_attribute_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: { + /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See + * comment #extract_attr_init. */ + return 3; + } + case CD_PROP_FLOAT2: { + return 2; + } + case CD_PROP_FLOAT3: { + return 3; + } + case CD_PROP_COLOR: { + return 4; + } + default: { + return 0; + } + } +} + +static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_FETCH_INT_TO_FLOAT; + } + case CD_PROP_COLOR: { + return GPU_FETCH_INT_TO_FLOAT_UNIT; + } + default: { + return GPU_FETCH_FLOAT; + } + } +} + +static GPUVertCompType get_comp_type_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_COMP_I32; + } + case CD_PROP_COLOR: { + return GPU_COMP_U16; + } + default: { + return GPU_COMP_F32; + } + } +} + +static void init_vbo_for_attribute(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type); + GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type); + const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type); + /* We should not be here if the attribute type is not supported. */ + BLI_assert(comp_size != 0); + + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name( + custom_data, request.cd_type, request.layer_index); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, static_cast<uint32_t>(mr->loop_len)); +} + +template<typename AttributeType, typename VBOType> +static void fill_vertbuf_with_attribute(const MeshRenderData *mr, + VBOType *vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + const MPoly *mpoly = mr->mpoly; + const MLoop *mloop = mr->mloop; + + const AttributeType *attr_data = static_cast<AttributeType *>( + CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); + + using converter = attribute_type_converter<AttributeType, VBOType>; + + switch (request.domain) { + default: { + BLI_assert(false); + break; + } + case ATTR_DOMAIN_POINT: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->v]); + } + break; + } + case ATTR_DOMAIN_CORNER: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { + *vbo_data = converter::convert_value(attr_data[ml_index]); + } + break; + } + case ATTR_DOMAIN_EDGE: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->e]); + } + break; + } + case ATTR_DOMAIN_FACE: { + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly &poly = mpoly[mp_index]; + const VBOType value = converter::convert_value(attr_data[mp_index]); + for (int l = 0; l < poly.totloop; l++) { + *vbo_data++ = value; + } + } + break; + } + } +} + +template<typename AttributeType, typename VBOType> +static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, + VBOType *&vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + + using converter = attribute_type_converter<AttributeType, VBOType>; + + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const AttributeType *attr_data = nullptr; + if (request.domain == ATTR_DOMAIN_POINT) { + attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_CORNER) { + attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_FACE) { + attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_EDGE) { + attr_data = static_cast<const AttributeType *>(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); + } + else { + BLI_assert(false); + continue; + } + *vbo_data = converter::convert_value(*attr_data); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } +} + +template<typename AttributeType, typename VBOType = AttributeType> +static void extract_attr_generic(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + VBOType *vbo_data = static_cast<VBOType *>(GPU_vertbuf_get_data(vbo)); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + fill_vertbuf_with_attribute_bm<AttributeType>(mr, vbo_data, request); + } + else { + fill_vertbuf_with_attribute<AttributeType>(mr, vbo_data, request); + } +} + +static void extract_attr_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data), + int index) +{ + const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_AttributeRequest &request = attrs_used->requests[index]; + + GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); + + init_vbo_for_attribute(mr, vbo, request); + + /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by + * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the + * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar + * texture as for volume attribute, so we can control the conversion ourselves. */ + switch (request.cd_type) { + case CD_PROP_BOOL: { + extract_attr_generic<bool, float3>(mr, vbo, request); + break; + } + case CD_PROP_INT32: { + extract_attr_generic<int32_t, float3>(mr, vbo, request); + break; + } + case CD_PROP_FLOAT: { + extract_attr_generic<float, float3>(mr, vbo, request); + break; + } + case CD_PROP_FLOAT2: { + extract_attr_generic<float2>(mr, vbo, request); + break; + } + case CD_PROP_FLOAT3: { + extract_attr_generic<float3>(mr, vbo, request); + break; + } + case CD_PROP_COLOR: { + extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request); + break; + } + default: { + BLI_assert(false); + } + } +} + +/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to + * extract. The overall API does not allow us to pass this in a convenient way. */ +#define EXTRACT_INIT_WRAPPER(index) \ + static void extract_attr_init##index( \ + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \ + { \ + extract_attr_init(mr, cache, buf, tls_data, index); \ + } + +EXTRACT_INIT_WRAPPER(0) +EXTRACT_INIT_WRAPPER(1) +EXTRACT_INIT_WRAPPER(2) +EXTRACT_INIT_WRAPPER(3) +EXTRACT_INIT_WRAPPER(4) +EXTRACT_INIT_WRAPPER(5) +EXTRACT_INIT_WRAPPER(6) +EXTRACT_INIT_WRAPPER(7) +EXTRACT_INIT_WRAPPER(8) +EXTRACT_INIT_WRAPPER(9) +EXTRACT_INIT_WRAPPER(10) +EXTRACT_INIT_WRAPPER(11) +EXTRACT_INIT_WRAPPER(12) +EXTRACT_INIT_WRAPPER(13) +EXTRACT_INIT_WRAPPER(14) + +template<int index> constexpr MeshExtract create_extractor_attr(ExtractInitFn fn) +{ + MeshExtract extractor = {nullptr}; + extractor.init = fn; + extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + return extractor; +} + +/** \} */ + +} // namespace blender::draw + +extern "C" { +#define CREATE_EXTRACTOR_ATTR(index) \ + blender::draw::create_extractor_attr<index>(blender::draw::extract_attr_init##index) + +const MeshExtract extract_attr[GPU_MAX_ATTR] = { + CREATE_EXTRACTOR_ATTR(0), + CREATE_EXTRACTOR_ATTR(1), + CREATE_EXTRACTOR_ATTR(2), + CREATE_EXTRACTOR_ATTR(3), + CREATE_EXTRACTOR_ATTR(4), + CREATE_EXTRACTOR_ATTR(5), + CREATE_EXTRACTOR_ATTR(6), + CREATE_EXTRACTOR_ATTR(7), + CREATE_EXTRACTOR_ATTR(8), + CREATE_EXTRACTOR_ATTR(9), + CREATE_EXTRACTOR_ATTR(10), + CREATE_EXTRACTOR_ATTR(11), + CREATE_EXTRACTOR_ATTR(12), + CREATE_EXTRACTOR_ATTR(13), + CREATE_EXTRACTOR_ATTR(14), +}; +} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 2c7770c8e72..f8878eb2617 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -43,9 +43,7 @@ static void extract_vcol_init(const MeshRenderData *mr, GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -64,42 +62,14 @@ static void extract_vcol_init(const MeshRenderData *mr, } /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); GPU_vertformat_alias_add(&format, attr_name); } } } - /* Sculpt Vertex Colors */ - if (U.experimental.use_sculpt_vertex_colors) { - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); - } - } - } - } - GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); @@ -108,7 +78,6 @@ static void extract_vcol_init(const MeshRenderData *mr, }; gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -139,35 +108,6 @@ static void extract_vcol_init(const MeshRenderData *mr, } } } - - if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); - } - } - } } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index afbd9b2c92d..69fabd004cc 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -51,6 +51,7 @@ #include "BKE_mask.h" #include "BKE_nla.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -2522,10 +2523,10 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Find / Set Filter Operator ******************** */ +/* ****************** Select Filter Textbox Operator ******************** */ /* XXX: make this generic? */ -static bool animchannels_find_poll(bContext *C) +static bool animchannels_select_filter_poll(bContext *C) { ScrArea *area = CTX_wm_area(C); @@ -2537,64 +2538,62 @@ static bool animchannels_find_poll(bContext *C) return ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA); } -/* find_invoke() - Get initial channels */ -static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int animchannels_select_filter_invoke(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *UNUSED(event)) { - bAnimContext ac; + ScrArea *area = CTX_wm_area(C); + ARegion *region_ctx = CTX_wm_region(C); + ARegion *region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS); - /* get editor data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return OPERATOR_CANCELLED; + CTX_wm_region_set(C, region_channels); + + /* Show the channel region if it's hidden. This means that direct activation of the input field + * is impossible, as it may not exist yet. For that reason, the actual activation is deferred to + * the modal callback function; by the time it runs, the screen has been redrawn and the UI + * element is there to activate. */ + if (region_channels->flag & RGN_FLAG_HIDDEN) { + ED_region_toggle_hidden(C, region_channels); + ED_region_tag_redraw(region_channels); } - /* set initial filter text, and enable filter */ - RNA_string_set(op->ptr, "query", ac.ads->searchstr); + WM_event_add_modal_handler(C, op); - /* defer to popup */ - return WM_operator_props_popup(C, op, event); + CTX_wm_region_set(C, region_ctx); + return OPERATOR_RUNNING_MODAL; } -/* find_exec() - Called to set the value */ -static int animchannels_find_exec(bContext *C, wmOperator *op) +static int animchannels_select_filter_modal(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) { bAnimContext ac; - - /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { return OPERATOR_CANCELLED; } - /* update filter text */ - RNA_string_get(op->ptr, "query", ac.ads->searchstr); - - /* redraw */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + ARegion *region = CTX_wm_region(C); + if (UI_textbutton_activate_rna(C, region, ac.ads, "filter_text")) { + /* Redraw to make sure it shows the cursor after activating */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } return OPERATOR_FINISHED; } -static void ANIM_OT_channels_find(wmOperatorType *ot) +static void ANIM_OT_channels_select_filter(wmOperatorType *ot) { /* identifiers */ - ot->name = "Find Channels"; - ot->idname = "ANIM_OT_channels_find"; - ot->description = "Filter the set of channels shown to only include those with matching names"; + ot->name = "Filter Channels"; + ot->idname = "ANIM_OT_channels_select_filter"; + ot->description = + "Start entering text which filters the set of channels shown to only include those with " + "matching names"; /* callbacks */ - ot->invoke = animchannels_find_invoke; - ot->exec = animchannels_find_exec; - ot->poll = animchannels_find_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_string(ot->srna, - "query", - "Query", - sizeof(((bDopeSheet *)NULL)->searchstr), - "", - "Text to search for in channel names"); + ot->invoke = animchannels_select_filter_invoke; + ot->modal = animchannels_select_filter_modal; + ot->poll = animchannels_select_filter_poll; } /* ********************** Select All Operator *********************** */ @@ -3563,7 +3562,7 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channel_select_keys); WM_operatortype_append(ANIM_OT_channels_rename); - WM_operatortype_append(ANIM_OT_channels_find); + WM_operatortype_append(ANIM_OT_channels_select_filter); WM_operatortype_append(ANIM_OT_channels_setting_enable); WM_operatortype_append(ANIM_OT_channels_setting_disable); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index bd799c00373..937385f9ffa 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -673,11 +673,7 @@ static EditBone *get_nearest_editbonepoint( } if (use_cycle) { - static int last_mval[2] = {-100, -100}; - if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) { - use_cycle = false; - } - copy_v2_v2_int(last_mval, vc->mval); + use_cycle = !WM_cursor_test_motion_and_update(vc->mval); } const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle); diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index 8b8fc4d3574..8da8fc0d6c9 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -37,3 +37,6 @@ void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogI void ED_asset_catalog_rename(AssetLibrary *library, blender::bke::CatalogID catalog_id, blender::StringRefNull new_name); +void ED_asset_catalog_move(AssetLibrary *library, + blender::bke::CatalogID src_catalog_id, + blender::bke::CatalogID dst_parent_catalog_id); diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h index bab1d1bf8a5..8e6a8e11d69 100644 --- a/source/blender/editors/asset/ED_asset_mark_clear.h +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -26,6 +26,7 @@ extern "C" { struct ID; struct bContext; +struct Main; /** * Mark the datablock as asset. @@ -52,6 +53,8 @@ void ED_asset_generate_preview(const struct bContext *C, struct ID *id); * \return whether the asset metadata was actually removed; false when the ID was not an asset. */ bool ED_asset_clear_id(struct ID *id); +void ED_assets_pre_save(struct Main *bmain); + bool ED_asset_can_mark_single_from_context(const struct bContext *C); #ifdef __cplusplus diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h index 5629ae189c0..36cbb4591e9 100644 --- a/source/blender/editors/asset/ED_asset_type.h +++ b/source/blender/editors/asset/ED_asset_type.h @@ -29,7 +29,8 @@ extern "C" { struct ID; bool ED_asset_type_id_is_non_experimental(const struct ID *id); -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS (FILTER_ID_MA | FILTER_ID_AC | FILTER_ID_WO) +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \ + (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO) /** * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, @@ -51,7 +52,7 @@ int64_t ED_asset_types_supported_as_filter_flags(void); * strings with this (not all UI code supports dynamic strings nicely). * Should start with a consonant, so usages can prefix it with "a" (not "an"). */ -#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Pose Action, or World" +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World" #ifdef __cplusplus } diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index dae960cbb0a..8e1e5be2e47 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -107,17 +107,44 @@ void ED_asset_catalog_rename(::AssetLibrary *library, AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); - AssetCatalogPath new_path = catalog->path.parent(); - new_path = new_path / StringRef(new_name); + const AssetCatalogPath new_path = catalog->path.parent() / StringRef(new_name); + const AssetCatalogPath clean_new_path = new_path.cleanup(); - if (new_path == catalog->path) { + if (new_path == catalog->path || clean_new_path == catalog->path) { /* Nothing changed, so don't bother renaming for nothing. */ return; } catalog_service->undo_push(); catalog_service->tag_has_unsaved_changes(catalog); - catalog_service->update_catalog_path(catalog_id, new_path); + catalog_service->update_catalog_path(catalog_id, clean_new_path); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void ED_asset_catalog_move(::AssetLibrary *library, + const CatalogID src_catalog_id, + const CatalogID dst_parent_catalog_id) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id); + AssetCatalog *dst_catalog = catalog_service->find_catalog(dst_parent_catalog_id); + + const AssetCatalogPath new_path = dst_catalog->path / StringRef(src_catalog->path.name()); + const AssetCatalogPath clean_new_path = new_path.cleanup(); + + if (new_path == src_catalog->path || clean_new_path == src_catalog->path) { + /* Nothing changed, so don't bother renaming for nothing. */ + return; + } + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(src_catalog); + catalog_service->update_catalog_path(src_catalog_id, clean_new_path); WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); } diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index 4be7376a1c3..eb254dcd28b 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -25,7 +25,9 @@ #include "BKE_asset.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "BKE_main.h" #include "BLO_readfile.h" @@ -52,7 +54,9 @@ bool ED_asset_mark_id(ID *id) id_fake_user_set(id); + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); id->asset_data = BKE_asset_metadata_create(); + id->asset_data->local_type_info = id_type_info->asset_type_info; /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); @@ -79,6 +83,21 @@ bool ED_asset_clear_id(ID *id) return true; } +void ED_assets_pre_save(struct Main *bmain) +{ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (!id->asset_data || !id->asset_data->local_type_info) { + continue; + } + + if (id->asset_data->local_type_info->pre_save_fn) { + id->asset_data->local_type_info->pre_save_fn(id, id->asset_data); + } + } + FOREACH_MAIN_ID_END; +} + bool ED_asset_can_mark_single_from_context(const bContext *C) { /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index e2ae3b3893b..d2fd8ab88a4 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -427,7 +427,12 @@ static int asset_catalog_new_exec(bContext *C, wmOperator *op) struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr); - ED_asset_catalog_add(asset_library, "Catalog", parent_path); + blender::bke::AssetCatalog *new_catalog = ED_asset_catalog_add( + asset_library, "Catalog", parent_path); + + if (sfile) { + ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id); + } MEM_freeN(parent_path); @@ -554,7 +559,7 @@ static bool asset_catalog_redo_poll(bContext *C) static void ASSET_OT_catalog_redo(struct wmOperatorType *ot) { /* identifiers */ - ot->name = "redo Catalog Edits"; + ot->name = "Redo Catalog Edits"; ot->description = "Redo the last undone edit to the asset catalogs"; ot->idname = "ASSET_OT_catalog_redo"; diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc index cdff538a712..028c0cb9ffc 100644 --- a/source/blender/editors/asset/intern/asset_type.cc +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -30,7 +30,7 @@ bool ED_asset_type_id_is_non_experimental(const ID *id) { /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ - return ELEM(GS(id->name), ID_MA, ID_AC, ID_WO); + return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO); } bool ED_asset_type_is_supported(const ID *id) diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index 07117c0153b..aed58e31798 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -220,7 +220,7 @@ static void cage3d_draw_circle_wire(const float r[3], immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", line_width * U.pixelsize); - imm_draw_cube_wire_3d(pos, (float[3]){0}, r); + imm_draw_cube_wire_3d(pos, (const float[3]){0}, r); #if 0 if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index 33532bd0549..93ee6ec2d81 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -225,8 +225,10 @@ static void snap_gizmo_setup(wmGizmo *gz) gz->flag |= WM_GIZMO_NO_TOOLTIP; SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; snap_gizmo->snap_state = ED_view3d_cursor_snap_active(); - snap_gizmo->snap_state->draw_point = true; - snap_gizmo->snap_state->draw_plane = false; + if (snap_gizmo->snap_state) { + snap_gizmo->snap_state->draw_point = true; + snap_gizmo->snap_state->draw_plane = false; + } rgba_float_to_uchar(snap_gizmo->snap_state->color_point, gz->color); } @@ -284,7 +286,9 @@ static int snap_gizmo_invoke(bContext *UNUSED(C), static void snap_gizmo_free(wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); + if (snap_gizmo->snap_state) { + ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); + } } static void GIZMO_GT_snap_3d(wmGizmoType *gzt) diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 3beabaf2d1d..68b6e44371c 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -23,6 +23,8 @@ #pragma once +#include "DNA_uuid_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -145,7 +147,9 @@ bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile); struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); -/* Activate the file that corresponds to the given ID. +void ED_fileselect_activate_asset_catalog(const struct SpaceFile *sfile, bUUID catalog_id); + +/* Activate and select the file that corresponds to the given ID. * Pass deferred=true to wait for the next refresh before activating. */ void ED_fileselect_activate_by_id(struct SpaceFile *sfile, struct ID *asset_id, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 1d51a3e77cf..e68617f7867 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -49,7 +49,7 @@ typedef enum { NODE_RIGHT = 8, } NodeBorder; -#define NODE_GRID_STEPS 5 +#define NODE_GRID_STEP_SIZE 10 #define NODE_EDGE_PAN_INSIDE_PAD 2 #define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */ #define NODE_EDGE_PAN_SPEED_RAMP 1 @@ -64,7 +64,6 @@ void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]); int ED_node_tree_path_length(struct SpaceNode *snode); void ED_node_tree_path_get(struct SpaceNode *snode, char *value); -void ED_node_tree_path_get_fixedbuf(struct SpaceNode *snode, char *value, int max_length); void ED_node_tree_start(struct SpaceNode *snode, struct bNodeTree *ntree, diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 083d167c573..2a557f1abd3 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -122,6 +122,8 @@ void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChil struct Object *ob_parent_recurse, int mode); +void ED_object_xform_array_m4(struct Object **objects, uint objects_len, const float matrix[4][4]); + /* object_ops.c */ void ED_operatortypes_object(void); void ED_operatormacros_object(void); @@ -202,7 +204,7 @@ void ED_object_parent(struct Object *ob, const char *substr); char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, struct PointerRNA *properties, - const struct wmEvent *event); + const int mval[2]); /* bitflags for enter/exit editmode */ enum { diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b90c7f27c57..08b6c02a8d0 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -317,6 +317,7 @@ bool ED_operator_regionactive(struct bContext *C); bool ED_operator_scene(struct bContext *C); bool ED_operator_scene_editable(struct bContext *C); bool ED_operator_objectmode(struct bContext *C); +bool ED_operator_objectmode_poll_msg(struct bContext *C); bool ED_operator_view3d_active(struct bContext *C); bool ED_operator_region_view3d_active(struct bContext *C); diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 7002db163b6..62d1dfbf0b1 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -44,6 +44,7 @@ typedef enum { SNAP_NOT_SELECTED = 1, SNAP_NOT_ACTIVE = 2, SNAP_ONLY_ACTIVE = 3, + SNAP_SELECTABLE = 4, } eSnapSelect; typedef enum { diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 67c470a005f..6d20044d8cf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -276,12 +276,15 @@ typedef struct V3DSnapCursorState { eV3DPlaceOrient plane_orient; uchar color_line[4]; uchar color_point[4]; + uchar color_box[4]; float *prevpoint; + float box_dimensions[3]; short snap_elem_force; /* If zero, use scene settings. */ short plane_axis; bool use_plane_axis_auto; bool draw_point; bool draw_plane; + bool draw_box; } V3DSnapCursorState; void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state); @@ -293,7 +296,6 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, const struct bContext *C, const int x, const int y); - struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const float loc_prev[3], @@ -302,7 +304,6 @@ void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, const uchar color_line[4], const uchar color_point[4], const short snap_elem_type); -void ED_view3d_cursor_snap_exit(void); /* view3d_iterators.c */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 67d034f4ab6..725c9921d13 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -37,6 +37,7 @@ extern "C" { struct ARegion; struct AssetFilterSettings; struct AssetHandle; +struct AssetMetaData; struct AutoComplete; struct EnumPropertyItem; struct FileDirEntry; @@ -246,7 +247,7 @@ enum { #define UI_DEFAULT_TEXT_POINTS 11 /* Larger size used for title text. */ -#define UI_DEFAULT_TITLE_POINTS 12 +#define UI_DEFAULT_TITLE_POINTS 11 #define UI_PANEL_WIDTH 340 #define UI_COMPACT_PANEL_WIDTH 160 @@ -423,10 +424,6 @@ typedef enum eButGradientType { * Functions to draw various shapes, taking theme settings into account. * Used for code that draws its own UI style elements. */ -void UI_draw_anti_tria( - float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]); -void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]); - void UI_draw_roundbox_corner_set(int type); void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4]); void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4]); @@ -437,12 +434,6 @@ void UI_draw_roundbox_3ub_alpha(const struct rctf *rect, unsigned char alpha); void UI_draw_roundbox_3fv_alpha( const struct rctf *rect, bool filled, float rad, const float col[3], float alpha); -void UI_draw_roundbox_shade_x(const struct rctf *rect, - bool filled, - float rad, - float shadetop, - float shadedown, - const float col[4]); void UI_draw_roundbox_4fv_ex(const struct rctf *rect, const float inner1[4], const float inner2[4], @@ -785,6 +776,7 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id); void UI_but_drag_set_asset(uiBut *but, const struct AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, /* eFileAssetImportType */ int icon, struct ImBuf *imb, @@ -796,7 +788,8 @@ void UI_but_drag_set_value(uiBut *but); void UI_but_drag_set_image( uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free); -bool UI_but_active_drop_name(struct bContext *C); +uiBut *UI_but_active_drop_name_button(const struct bContext *C); +bool UI_but_active_drop_name(const struct bContext *C); bool UI_but_active_drop_color(struct bContext *C); void UI_but_flag_enable(uiBut *but, int flag); @@ -2687,7 +2680,12 @@ void UI_fontstyle_draw_simple_backdrop(const struct uiFontStyle *fs, const float col_fg[4], const float col_bg[4]); -int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str); +int UI_fontstyle_string_width(const struct uiFontStyle *fs, + const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2); +int UI_fontstyle_string_width_with_block_aspect(const struct uiFontStyle *fs, + const char *str, + const float aspect) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2); int UI_fontstyle_height_max(const struct uiFontStyle *fs); void UI_draw_icon_tri(float x, float y, char dir, const float[4]); @@ -2780,12 +2778,12 @@ void UI_interface_tag_script_reload(void); bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const struct wmDrag *drag); +bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags); -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, - const struct bContext *C, - const struct wmDrag *drag, - const struct wmEvent *event); bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 5edccfa8c88..b14ee6c4a59 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -23,15 +23,40 @@ #include <memory> #include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" namespace blender::nodes::geometry_nodes_eval_log { struct GeometryAttributeInfo; } struct uiBlock; +struct StructRNA; +struct uiSearchItems; + namespace blender::ui { + class AbstractTreeView; +/** + * An item in a breadcrumb-like context. Currently this struct is very simple, but more + * could be added to it in the future, to support interactivity or tooltips, for example. + */ +struct ContextPathItem { + /* Text to display in the UI. */ + std::string name; + /* #BIFIconID */ + int icon; +}; + +void context_path_add_generic(Vector<ContextPathItem> &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override = ICON_NONE); + +void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path); + void attribute_search_add_items( StringRefNull str, const bool is_output, diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index b1ec22c57a6..0d18eedeac9 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -48,6 +48,8 @@ namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; +class AbstractTreeViewItemDropController; +class AbstractTreeViewItemDragController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -242,17 +244,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * arguments for checking if the item is currently in an active state. */ virtual void is_active(IsActiveFn is_active_fn); - virtual bool on_drop(const wmDrag &drag); - virtual bool can_drop(const wmDrag &drag) const; - /** - * Custom text to display when dragging over a tree item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() - * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. - */ - virtual std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const; + /** * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if * another item is already being renamed. @@ -282,6 +274,20 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual bool matches(const AbstractTreeViewItem &other) const; + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractTreeViewItemDragController. + */ + virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractTreeViewItemDropController. + * + * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const; + void begin_renaming(); void end_renaming(); @@ -344,6 +350,62 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** \} */ /* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to enable dragging a tree-item. An item can return a drop controller for itself via a + * custom implementation of #AbstractTreeViewItem::create_drag_controller(). + */ +class AbstractTreeViewItemDragController { + public: + virtual ~AbstractTreeViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; +}; + +/** + * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this + * item. An item can return a drop controller for itself via a custom implementation of + * #AbstractTreeViewItem::create_drop_controller(). + */ +class AbstractTreeViewItemDropController { + protected: + AbstractTreeView &tree_view_; + + public: + AbstractTreeViewItemDropController(AbstractTreeView &tree_view); + virtual ~AbstractTreeViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * Custom text to display when dragging over a tree item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(const wmDrag &drag) = 0; + + template<class TreeViewType> inline TreeViewType &tree_view() const; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ /** \name Predefined Tree-View Item Types * * Common, Basic Tree-View Item Types. @@ -357,9 +419,10 @@ class BasicTreeViewItem : public AbstractTreeViewItem { using ActivateFn = std::function<void(BasicTreeViewItem &new_active)>; BIFIconID icon; - BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); + explicit BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); void build_row(uiLayout &row) override; + void add_label(uiLayout &layout, StringRefNull label_override = ""); void on_activate(ActivateFn fn); protected: @@ -390,4 +453,11 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...))); } +template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const +{ + static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value, + "Type must derive from and implement the AbstractTreeView interface"); + return static_cast<TreeViewType &>(tree_view_); +} + } // namespace blender::ui diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 13895879f01..122e5a7d839 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -147,6 +147,10 @@ void UI_view2d_view_restore(const struct bContext *C); /* grid drawing */ void UI_view2d_multi_grid_draw( const struct View2D *v2d, int colorid, float step, int level_size, int totlevels); +void UI_view2d_dot_grid_draw(const struct View2D *v2d, + int grid_color_id, + float step, + int grid_levels); void UI_view2d_draw_lines_y__values(const struct View2D *v2d); void UI_view2d_draw_lines_x__values(const struct View2D *v2d); @@ -311,6 +315,9 @@ typedef struct View2DEdgePanData { /** View2d we're operating in. */ struct View2D *v2d; + /** Panning should only start once being in the inside rect once (e.g. adding nodes can happen + * outside). */ + bool enabled; /** Inside distance in UI units from the edge of the region within which to start panning. */ float inside_pad; /** Outside distance in UI units from the edge of the region at which to stop panning. */ diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index b2659f5ed52..84172c7efce 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC interface_anim.c interface_button_group.c interface_context_menu.c + interface_context_path.cc interface_draw.c interface_dropboxes.cc interface_eyedropper.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 5068969946a..62c84ed38ff 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6231,12 +6231,13 @@ void UI_but_drag_set_id(uiBut *but, ID *id) void UI_but_drag_set_asset(uiBut *but, const AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, int icon, struct ImBuf *imb, float scale) { - wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, path, import_type); + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the * #wmDropBox. diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc new file mode 100644 index 00000000000..b0f8d186afa --- /dev/null +++ b/source/blender/editors/interface/interface_context_path.cc @@ -0,0 +1,85 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_vector.hh" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "WM_api.h" + +namespace blender::ui { + +void context_path_add_generic(Vector<ContextPathItem> &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override) +{ + /* Add the null check here to make calling functions less verbose. */ + if (!ptr) { + return; + } + + PointerRNA rna_ptr; + RNA_pointer_create(nullptr, &rna_type, ptr, &rna_ptr); + char name[128]; + RNA_struct_name_get_alloc(&rna_ptr, name, sizeof(name), nullptr); + + /* Use a blank icon by default to check whether to retrieve it automatically from the type. */ + const BIFIconID icon = icon_override == ICON_NONE ? + static_cast<BIFIconID>(RNA_struct_ui_icon(rna_ptr.type)) : + icon_override; + + path.append({name, static_cast<int>(icon)}); +} + +/* -------------------------------------------------------------------- */ +/** \name Breadcrumb Template + * \{ */ + +void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path) +{ + uiLayout *row = uiLayoutRow(&layout, true); + uiLayoutSetAlignment(&layout, UI_LAYOUT_ALIGN_LEFT); + + for (const int i : context_path.index_range()) { + uiLayout *sub_row = uiLayoutRow(row, true); + uiLayoutSetAlignment(sub_row, UI_LAYOUT_ALIGN_LEFT); + + if (i > 0) { + uiItemL(sub_row, "", ICON_RIGHTARROW_THIN); + } + uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon); + } +} + +} // namespace blender::ui + +/** \} */
\ No newline at end of file diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 6cb0fcd499c..e45a5fc61c6 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -178,35 +178,6 @@ void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float UI_draw_roundbox_4fv_ex(rect, (filled) ? col : NULL, NULL, 1.0f, col, U.pixelsize, rad); } -/* linear horizontal shade within button or in outline */ -/* view2d scrollers use it */ -void UI_draw_roundbox_shade_x( - const rctf *rect, bool filled, float rad, float shadetop, float shadedown, const float col[4]) -{ - float inner1[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float inner2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float outline[4]; - - if (filled) { - inner1[0] = min_ff(1.0f, col[0] + shadetop); - inner1[1] = min_ff(1.0f, col[1] + shadetop); - inner1[2] = min_ff(1.0f, col[2] + shadetop); - inner1[3] = 1.0f; - inner2[0] = max_ff(0.0f, col[0] + shadedown); - inner2[1] = max_ff(0.0f, col[1] + shadedown); - inner2[2] = max_ff(0.0f, col[2] + shadedown); - inner2[3] = 1.0f; - } - - /* TODO: non-filled box don't have gradients. Just use middle color. */ - outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f); - outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f); - outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f); - outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f); - - UI_draw_roundbox_4fv_ex(rect, inner1, inner2, 1.0f, outline, U.pixelsize, rad); -} - void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { const int ofs_y = 4 * U.pixelsize; diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 62250a34cf4..81a1354cbe7 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -22,6 +22,10 @@ #include "DNA_space_types.h" +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + #include "WM_api.h" #include "UI_interface.h" @@ -35,24 +39,43 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve return false; } - return UI_tree_view_item_can_drop(hovered_tree_item, drag); + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + + drag->free_disabled_info = false; + return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->disabled_info); } static char *ui_tree_view_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int xy[2], wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, - event->xy); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); if (!hovered_tree_item) { return nullptr; } - return UI_tree_view_item_drop_tooltip(hovered_tree_item, C, drag, event); + return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); } +/* ---------------------------------------------------------------------- */ + +static bool ui_drop_name_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + return UI_but_active_drop_name(C) && (drag->type == WM_DRAG_ID); +} + +static void ui_drop_name_copy(wmDrag *drag, wmDropBox *drop) +{ + const ID *id = WM_drag_get_local_ID(drag, 0); + RNA_string_set(drop->ptr, "string", id->name + 2); +} + +/* ---------------------------------------------------------------------- */ + void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); @@ -63,4 +86,10 @@ void ED_dropboxes_ui() nullptr, nullptr, ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, + "UI_OT_drop_name", + ui_drop_name_poll, + ui_drop_name_copy, + WM_drag_free_imported_drag_ID, + nullptr); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 52ab13e5cd0..51ebe5399b3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2145,6 +2145,12 @@ static bool ui_but_drag_init(bContext *C, return false; } } + else if (but->type == UI_BTYPE_TREEROW) { + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + if (tree_row_but->tree_item) { + UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + } + } else { wmDrag *drag = WM_event_start_drag( C, @@ -2437,39 +2443,6 @@ static void ui_apply_but( /** \} */ /* -------------------------------------------------------------------- */ -/** \name Button Drop Event - * \{ */ - -/* only call if event type is EVT_DROP */ -static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data) -{ - ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ - - LISTBASE_FOREACH (wmDrag *, wmd, drags) { - /* TODO: asset dropping. */ - if (wmd->type == WM_DRAG_ID) { - /* align these types with UI_but_active_drop_name */ - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - ID *id = WM_drag_get_local_ID(wmd, 0); - - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - - ui_textedit_string_set(but, data, id->name + 2); - - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Button Copy & Paste * \{ */ @@ -2666,15 +2639,9 @@ static void ui_but_copy_text(uiBut *but, char *output, int output_len_max) static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - ui_textedit_string_set(but, but->active, buf_paste); - - if (but->type == UI_BTYPE_SEARCH_MENU) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); + BLI_assert(but->active == data); + UNUSED_VARS_NDEBUG(data); + ui_but_set_string_interactive(C, but, buf_paste); } static void ui_but_copy_colorband(uiBut *but) @@ -3018,6 +2985,24 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], /** \name Button Text Selection/Editing * \{ */ +/** + * Use handling code to set a string for the button. Handles the case where the string is set for a + * search button while the search menu is open, so the results are updated accordingly. + * This is basically the same as pasting the string into the button. + */ +void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) +{ + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + ui_textedit_string_set(but, but->active, value); + + if (but->type == UI_BTYPE_SEARCH_MENU && but->active) { + but->changed = true; + ui_searchbox_update(C, but->active->searchbox, but, true); + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); +} + void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but) { if (!but->active) { @@ -4821,19 +4806,33 @@ static int ui_do_but_TREEROW(bContext *C, if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { - if (event->val == KM_CLICK) { - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - } - if (event->val == KM_DBL_CLICK) { - data->cancel = true; + switch (event->val) { + case KM_PRESS: + /* Extra icons have priority, don't mess with them. */ + if (ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + return WM_UI_HANDLER_BREAK; + } + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + return WM_UI_HANDLER_CONTINUE; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - ED_region_tag_redraw(CTX_wm_region(C)); - return WM_UI_HANDLER_BREAK; + case KM_CLICK: + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + + case KM_DBL_CLICK: + data->cancel = true; + UI_tree_view_item_begin_rename(tree_row_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; } } } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* Let "default" button handling take care of the drag logic. */ + return ui_do_but_EXIT(C, but, data, event); + } return WM_UI_HANDLER_CONTINUE; } @@ -7929,7 +7928,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Only hard-coded stuff here, button interactions with configurable * keymaps are handled using operators (see #ED_keymap_ui). */ - if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) { + if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && @@ -7978,11 +7977,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * return WM_UI_HANDLER_BREAK; } - /* handle drop */ - if (event->type == EVT_DROP) { - ui_but_drop(C, event, but, data); - } - if ((data->state == BUTTON_STATE_HIGHLIGHT) && ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_RELEASE) && @@ -10648,7 +10642,8 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) { + else if (saferct && !BLI_rctf_isect_pt( + &saferct->parent, (float)event->xy[0], (float)event->xy[1])) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } @@ -10779,7 +10774,7 @@ static int ui_handle_menu_event(bContext *C, } #endif - /* Don't handle double click events, rehandle as regular press/release. */ + /* Don't handle double click events, re-handle as regular press/release. */ if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) { return retval; } @@ -11695,20 +11690,25 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen) } } -/* returns true if highlighted button allows drop of names */ -/* called in region context */ -bool UI_but_active_drop_name(bContext *C) +uiBut *UI_but_active_drop_name_button(const bContext *C) { ARegion *region = CTX_wm_region(C); uiBut *but = ui_region_find_active_but(region); if (but) { if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - return true; + return but; } } - return false; + return NULL; +} + +/* returns true if highlighted button allows drop of names */ +/* called in region context */ +bool UI_but_active_drop_name(const bContext *C) +{ + return UI_but_active_drop_name_button(C) != NULL; } bool UI_but_active_drop_color(bContext *C) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 0826157b5e6..f766bb1465f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -680,6 +680,7 @@ extern bool ui_but_string_eval_number(struct bContext *C, extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); +extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); void ui_def_but_icon(uiBut *but, const int icon, const int flag); @@ -1031,7 +1032,6 @@ enum { struct GPUBatch *ui_batch_roundbox_widget_get(void); struct GPUBatch *ui_batch_roundbox_shadow_get(void); -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); void ui_draw_popover_back(struct ARegion *region, struct uiStyle *style, diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index e54b261facd..25ba0e13487 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -351,12 +351,16 @@ static int ui_text_icon_width_ex(uiLayout *layout, if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_FIXED_SIZE; } - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + float margin = pad_factor->text; if (icon) { margin += pad_factor->icon; } - return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin); + + const float aspect = layout->root->block->aspect; + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) + + (int)ceilf(unit_x * margin); } return unit_x * 10; } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1a1d52b0425..c962a1107ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1864,6 +1864,39 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Drop Name Operator + * \{ */ + +static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + uiBut *but = UI_but_active_drop_name_button(C); + char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL); + + if (str) { + ui_but_set_string_interactive(C, but, str); + MEM_freeN(str); + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_drop_name(wmOperatorType *ot) +{ + ot->name = "Drop Name"; + ot->idname = "UI_OT_drop_name"; + ot->description = "Drop name to button"; + + ot->poll = ED_operator_regionactive; + ot->invoke = drop_name_invoke; + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "string", NULL, 0, "String", "The string value to drop into the button"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name UI List Search Operator * \{ */ @@ -2025,6 +2058,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_jump_to_target_button); WM_operatortype_append(UI_OT_drop_color); + WM_operatortype_append(UI_OT_drop_name); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index a22351eea7e..072362492d8 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1550,7 +1550,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) } BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); - BLF_color3ubv(fontid, theme_col_text); + BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text); BLF_draw(fontid, category_id_draw, category_draw_len); GPU_blend(GPU_BLEND_NONE); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 6b1ff92a855..92a9f14c77d 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -376,6 +376,37 @@ int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } +/** + * Return the width of `str` with the spacing & kerning of `fs` with `aspect` + * (representing #uiBlock.aspect) applied. + * + * When calculating text width, the UI layout logic calculate widths without scale, + * only applying scale when drawing. This causes problems for fonts since kerning at + * smaller sizes often makes them wider than a scaled down version of the larger text. + * Resolve this by calculating the text at the on-screen size, + * returning the result scaled back to 1:1. See T92361. + */ +int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, + const char *str, + const float aspect) +{ + uiFontStyle fs_buf; + if (aspect != 1.0f) { + fs_buf = *fs; + ui_fontscale(&fs_buf.points, aspect); + fs = &fs_buf; + } + + int width = UI_fontstyle_string_width(fs, str); + + if (aspect != 1.0f) { + /* While in most cases rounding up isn't important, it can make a difference + * with small fonts (3px or less), zooming out in the node-editor for e.g. */ + width = (int)ceilf(width * aspect); + } + return width; +} + int UI_fontstyle_height_max(const uiFontStyle *fs) { UI_fontstyle_set(fs); diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index f27b37a27de..d3ce7ebc3db 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -70,6 +70,7 @@ static void asset_view_item_but_drag_set(uiBut *but, UI_but_drag_set_asset(but, asset_handle, BLI_strdup(blend_path), + ED_asset_handle_get_metadata(asset_handle), FILE_ASSET_IMPORT_APPEND, ED_asset_handle_get_preview_icon_id(asset_handle), imbuf, diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc index 0157d0b66a3..85a6147432b 100644 --- a/source/blender/editors/interface/interface_template_attribute_search.cc +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -80,9 +80,10 @@ void attribute_search_add_items(StringRefNull str, break; } } - if (!contained && is_output) { + if (!contained) { dummy_info.name = str; - UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_ADD, 0, 0); + UI_search_item_add( + seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0); } } @@ -122,4 +123,4 @@ void attribute_search_add_items(StringRefNull str, BLI_string_search_free(search); } -} // namespace blender::ui
\ No newline at end of file +} // namespace blender::ui diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e9acc65ed37..3e9042d29a0 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -518,7 +518,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void) /** \name Draw Triangle Arrow * \{ */ -void UI_draw_anti_tria( +static void draw_anti_tria( float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]) { const float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}}; @@ -559,66 +559,31 @@ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) const float f7 = 0.25 * U.widget_unit; if (dir == 'h') { - UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); + draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); } else if (dir == 't') { - UI_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); + draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); } else { /* 'v' = vertical, down. */ - UI_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); + draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); } } /* triangle 'icon' inside rect */ -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) +static void draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { if (dir == 'h') { const float half = 0.5f * BLI_rctf_size_y(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } else { const float half = 0.5f * BLI_rctf_size_x(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); } } -void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) -{ - float draw_color[4]; - - copy_v4_v4(draw_color, color); - draw_color[3] *= 2.0f / WIDGET_AA_JITTER; - - GPU_blend(GPU_BLEND_ALPHA); - - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor4fv(draw_color); - - /* for each AA step */ - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - immBegin(GPU_PRIM_TRI_FAN, length); - immVertex2f(pos, tri_array[0][0], tri_array[0][1]); - immVertex2f(pos, tri_array[1][0], tri_array[1][1]); - - /* We jitter only the middle of the fan, the extremes are pinned. */ - for (int i = 2; i < length - 1; i++) { - immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]); - } - - immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]); - immEnd(); - } - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); -} - static void widget_init(uiWidgetBase *wtb) { wtb->totvert = wtb->halfwayvert = 0; @@ -1494,7 +1459,7 @@ static void widget_draw_submenu_tria(const uiBut *but, GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); GPU_blend(GPU_BLEND_NONE); - ui_draw_anti_tria_rect(&tria_rect, 'h', col); + draw_anti_tria_rect(&tria_rect, 'h', col); } static void ui_text_clip_give_prev_off(uiBut *but, const char *str) @@ -3721,7 +3686,7 @@ static void widget_datasetrow( static void widget_nodesocket( uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { - const int radi = 5; + const int radi = 0.25f * BLI_rcti_size_y(rect); uiWidgetBase wtb; widget_init(&wtb); @@ -4632,6 +4597,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu switch (but->type) { case UI_BTYPE_LABEL: wt = widget_type(UI_WTYPE_ICON_LABEL); + if (!(but->flag & UI_HAS_ICON)) { + but->drawflag |= UI_BUT_NO_TEXT_PADDING; + } break; default: wt = widget_type(UI_WTYPE_ICON); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index ad7c6332ee9..aece2e58f1e 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -252,10 +252,11 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER_ACTIVE: cp = ts->header; + const int factor = 5; /* Lighten the header color when editor is active. */ - header_active[0] = cp[0] > 245 ? cp[0] - 10 : cp[0] + 10; - header_active[1] = cp[1] > 245 ? cp[1] - 10 : cp[1] + 10; - header_active[2] = cp[2] > 245 ? cp[2] - 10 : cp[2] + 10; + header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor; + header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor; + header_active[2] = cp[2] > 245 ? cp[2] - factor : cp[2] + factor; header_active[3] = cp[3]; cp = header_active; break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc index 88aa362deb5..c08fa51d5a5 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/tree_view.cc @@ -29,6 +29,7 @@ #include "UI_interface.h" +#include "WM_api.h" #include "WM_types.h" #include "UI_tree_view.hh" @@ -354,22 +355,18 @@ void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn) is_active_fn_ = is_active_fn; } -bool AbstractTreeViewItem::on_drop(const wmDrag & /*drag*/) +std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller() + const { - /* Do nothing by default. */ - return false; -} - -bool AbstractTreeViewItem::can_drop(const wmDrag & /*drag*/) const -{ - return false; + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; } -std::string AbstractTreeViewItem::drop_tooltip(const bContext & /*C*/, - const wmDrag & /*drag*/, - const wmEvent & /*event*/) const +std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller() + const { - return TIP_("Drop into/onto tree item"); + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; } bool AbstractTreeViewItem::can_rename() const @@ -553,6 +550,12 @@ void AbstractTreeViewItem::change_state_delayed() activate(); } } +/* ---------------------------------------------------------------------- */ + +AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) + : tree_view_(tree_view) +{ +} /* ---------------------------------------------------------------------- */ @@ -646,7 +649,18 @@ BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(ic void BasicTreeViewItem::build_row(uiLayout &row) { - uiItemL(&row, label_.c_str(), icon); + add_label(row); +} + +void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override) +{ + const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override; + + /* Some padding for labels without collapse chevron and no icon. Looks weird without. */ + if (icon == ICON_NONE && !is_collapsible()) { + uiItemS_ex(&layout, 0.8f); + } + uiItemL(&layout, label.c_str(), icon); } void BasicTreeViewItem::on_activate() @@ -680,19 +694,53 @@ bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, return a.matches_including_parents(b); } -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, const wmDrag *drag) +/** + * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't + * support dragging, i.e. it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. + */ +bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) +{ + const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); + const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + return true; +} + +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) { const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return item.can_drop(*drag); + const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(*drag, r_disabled_hint); } -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, - const bContext *C, - const wmDrag *drag, - const wmEvent *event) +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) { const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return BLI_strdup(item.drop_tooltip(*C, *drag, *event).c_str()); + const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return nullptr; + } + + return BLI_strdup(drop_controller->drop_tooltip(*drag).c_str()); } /** @@ -702,10 +750,13 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags) { AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_); + std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = + item.create_drop_controller(); + const char *disabled_hint_dummy = nullptr; LISTBASE_FOREACH (const wmDrag *, drag, drags) { - if (item.can_drop(*drag)) { - return item.on_drop(*drag); + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(*drag); } } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index ca96fde9810..eea6512f0f8 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -32,6 +32,7 @@ #include "DNA_userdef_types.h" #include "BLI_array.h" +#include "BLI_easing.h" #include "BLI_link_utils.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -166,7 +167,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll) scroll = view2d_scroll_mapped(v2d->scroll); - /* scrollers are based off regionsize + /* Scrollers are based off region-size: * - they can only be on one to two edges of the region they define * - if they overlap, they must not occupy the corners (which are reserved for other widgets) */ @@ -1291,6 +1292,114 @@ void UI_view2d_multi_grid_draw( immUnbindProgram(); } +static void grid_axis_start_and_count( + const float step, const float min, const float max, float *r_start, int *r_count) +{ + *r_start = min; + if (*r_start < 0.0f) { + *r_start += -(float)fmod(min, step); + } + else { + *r_start += step - (float)fabs(fmod(min, step)); + } + + if (*r_start > max) { + *r_count = 0; + } + else { + *r_count = (max - *r_start) / step + 1; + } +} + +typedef struct DotGridLevelInfo { + /* The factor applied to the #min_step argument. This could be easily computed in runtime, + * but seeing it together with the other values is helpful. */ + float step_factor; + /* The normalized zoom level at which the grid level starts to fade in. + * At lower zoom levels, the points will not be visible and the level will be skipped. */ + float fade_in_start_zoom; + /* The normalized zoom level at which the grid finishes fading in. + * At higher zoom levels, the points will be opaque. */ + float fade_in_end_zoom; +} DotGridLevelInfo; + +static const DotGridLevelInfo level_info[9] = { + {6.4f, -0.1f, 0.01f}, + {3.2f, 0.0f, 0.025f}, + {1.6f, 0.025f, 0.15f}, + {0.8f, 0.05f, 0.2f}, + {0.4f, 0.1f, 0.25f}, + {0.2f, 0.125f, 0.3f}, + {0.1f, 0.25f, 0.5f}, + {0.05f, 0.7f, 0.9f}, + {0.025f, 0.6f, 0.9f}, +}; + +/** + * Draw a multi-level grid of dots, with a dynamic number of levels based on the fading. + * + * \param grid_color_id: The theme color used for the points. Faded dynamically based on zoom. + * \param min_step: The base size of the grid. At different zoom levels, the visible grid may have + * a larger step size. + * \param grid_levels: The maximum grid depth. Larger grid levels will subdivide the grid more. + */ +void UI_view2d_dot_grid_draw(const View2D *v2d, + const int grid_color_id, + const float min_step, + const int grid_levels) +{ + BLI_assert(grid_levels > 0 && grid_levels < 10); + const float zoom_x = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur); + const float zoom_normalized = (zoom_x - v2d->minzoom) / (v2d->maxzoom - v2d->minzoom); + + GPUVertFormat *format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + GPU_point_size(3.0f * UI_DPI_FAC); + + float color[4]; + UI_GetThemeColor3fv(grid_color_id, color); + + for (int level = 0; level < grid_levels; level++) { + const DotGridLevelInfo *info = &level_info[level]; + const float step = min_step * info->step_factor * U.widget_unit; + + const float alpha_factor = (zoom_normalized - info->fade_in_start_zoom) / + (info->fade_in_end_zoom - info->fade_in_start_zoom); + color[3] = clamp_f(BLI_easing_cubic_ease_in_out(alpha_factor, 0.0f, 1.0f, 1.0f), 0.0f, 1.0f); + if (color[3] == 0.0f) { + break; + } + + int count_x; + float start_x; + grid_axis_start_and_count(step, v2d->cur.xmin, v2d->cur.xmax, &start_x, &count_x); + int count_y; + float start_y; + grid_axis_start_and_count(step, v2d->cur.ymin, v2d->cur.ymax, &start_y, &count_y); + if (count_x == 0 || count_y == 0) { + continue; + } + + immBegin(GPU_PRIM_POINTS, count_x * count_y); + + /* Theoretically drawing on top of lower grid levels could be avoided, but it would also + * increase the complexity of this loop, which isn't worth the time at the moment. */ + for (int i_y = 0; i_y < count_y; i_y++) { + const float y = start_y + step * i_y; + for (int i_x = 0; i_x < count_x; i_x++) { + const float x = start_x + step * i_x; + immAttr4fv(color_id, color); + immVertex2f(pos, x, y); + } + } + + immEnd(); + } + + immUnbindProgram(); +} /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index a49666ebbd3..8d8b9a4fe48 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -92,6 +92,8 @@ void UI_view2d_edge_pan_init(bContext *C, vpd->delay = delay; vpd->zoom_influence = zoom_influence; + vpd->enabled = false; + /* Calculate translation factor, based on size of view. */ const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1); const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); @@ -227,9 +229,16 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[ BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit); BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit); + /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start + * disabled). */ + if (BLI_rcti_isect_pt_v(&inside_rect, xy)) { + /* We are inside once, can start. */ + vpd->enabled = true; + } + int pan_dir_x = 0; int pan_dir_y = 0; - if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy)) { + if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) { /* Find whether the mouse is beyond X and Y edges. */ if (xy[0] > inside_rect.xmax) { pan_dir_x = 1; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index d073f5f2ba4..b712cfc24ed 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -184,8 +184,6 @@ typedef struct KnifeMeasureData { float cage[3]; float mval[2]; bool is_stored; - float corr_prev_cage[3]; /* "knife_start_cut" updates prev.cage breaking angle calculations, - * store correct version. */ } KnifeMeasureData; typedef struct KnifeUndoFrame { @@ -242,6 +240,7 @@ typedef struct KnifeTool_OpData { BLI_mempool *kverts; BLI_mempool *kedges; + bool no_cuts; /* A cut has not been made yet. */ BLI_Stack *undostack; BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */ @@ -496,7 +495,7 @@ static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) const int distance_precision = 4; /* Calculate distance and convert to string. */ - const float cut_len = len_v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage); + const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage); UnitSettings *unit = &kcd->scene->unit; if (unit->system == USER_UNIT_NONE) { @@ -703,7 +702,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) else { tempkfv = tempkfe->v2; } - angle = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, tempkfv->cageco); + angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco); if (angle < min_angle) { min_angle = angle; kfe = tempkfe; @@ -717,7 +716,7 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); knifetool_draw_angle(kcd, - kcd->mdata.corr_prev_cage, + kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, @@ -730,11 +729,11 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) kfe = kcd->curr.edge; /* Check for most recent cut (if cage is part of previous cut). */ - if (!compare_v3v3(kfe->v1->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG) && - !compare_v3v3(kfe->v2->cageco, kcd->mdata.corr_prev_cage, KNIFE_FLT_EPSBIG)) { + if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) && + !compare_v3v3(kfe->v2->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG)) { /* Determine acute angle. */ - float angle1 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v1->cageco); - float angle2 = angle_v3v3v3(kcd->mdata.corr_prev_cage, kcd->curr.cage, kfe->v2->cageco); + float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco); float angle; float *end; @@ -751,14 +750,8 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) float end_ss[2]; ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); - knifetool_draw_angle(kcd, - kcd->mdata.corr_prev_cage, - kcd->curr.cage, - end, - kcd->prev.mval, - kcd->curr.mval, - end_ss, - angle); + knifetool_draw_angle( + kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle); } } @@ -852,10 +845,10 @@ static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle); } else if (kcd->mdata.is_stored && !kcd->prev.is_space) { - float angle = angle_v3v3v3(kcd->curr.cage, kcd->mdata.corr_prev_cage, kcd->mdata.cage); + float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage); knifetool_draw_angle(kcd, kcd->curr.cage, - kcd->mdata.corr_prev_cage, + kcd->prev.cage, kcd->mdata.cage, kcd->curr.mval, kcd->prev.mval, @@ -1468,7 +1461,10 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); } -static void knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3]) +/* No longer used, but may be useful in the future. */ +static void UNUSED_FUNCTION(knifetool_recast_cageco)(KnifeTool_OpData *kcd, + float mval[3], + float r_cage[3]) { float origin[3]; float origin_ofs[3]; @@ -2396,7 +2392,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd) } /* Save values for angle drawing calculations. */ - copy_v3_v3(kcd->mdata.cage, kcd->mdata.corr_prev_cage); + copy_v3_v3(kcd->mdata.cage, kcd->prev.cage); copy_v2_v2(kcd->mdata.mval, kcd->prev.mval); kcd->mdata.is_stored = true; @@ -4094,6 +4090,8 @@ static void knifetool_init(bContext *C, knife_init_colors(&kcd->colors); } + kcd->no_cuts = true; + kcd->axis_string[0] = ' '; kcd->axis_string[1] = '\0'; @@ -4506,12 +4504,23 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) handled = true; break; case KNF_MODAL_NEW_CUT: + /* If no cuts have been made, exit. + * Preserves right click cancel workflow which most tools use, + * but stops accidentally deleting entire cuts with right click. + */ + if (kcd->no_cuts) { + ED_region_tag_redraw(kcd->region); + knifetool_exit(op); + ED_workspace_status_text(C, NULL); + return OPERATOR_CANCELLED; + } ED_region_tag_redraw(kcd->region); knife_finish_cut(kcd); kcd->mode = MODE_IDLE; handled = true; break; case KNF_MODAL_ADD_CUT: + kcd->no_cuts = false; knife_recalc_ortho(kcd); /* Get the value of the event which triggered this one. */ @@ -4525,16 +4534,6 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->init = kcd->curr; } - /* Preserve correct prev.cage for angle drawing calculations. */ - if (kcd->prev.edge == NULL && kcd->prev.vert == NULL) { - /* "knife_start_cut" moves prev.cage so needs to be recalculated. */ - /* Only occurs if prev was started on a face. */ - knifetool_recast_cageco(kcd, kcd->prev.mval, kcd->mdata.corr_prev_cage); - } - else { - copy_v3_v3(kcd->mdata.corr_prev_cage, kcd->prev.cage); - } - /* Freehand drawing is incompatible with cut-through. */ if (kcd->cut_through == false) { kcd->is_drag_hold = true; @@ -4811,7 +4810,7 @@ void MESH_OT_knife_tool(wmOperatorType *ot) "Occlude Geometry", "Only cut the front most geometry"); RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry"); - RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts through geometry"); + RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry"); RNA_def_enum(ot->srna, "visible_measurements", diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 2fcf8fa6f8f..e0768bcff24 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -884,9 +884,8 @@ static bool unified_findnearest(ViewContext *vc, BMFace **r_efa) { BMEditMesh *em = vc->em; - static short mval_prev[2] = {-1, -1}; - /* only cycle while the mouse remains still */ - const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1])); + + const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval); const float dist_init = ED_view3d_select_dist_px(); /* since edges select lines, we give dots advantage of ~20 pix */ const float dist_margin = (dist_init / 2); @@ -988,9 +987,6 @@ static bool unified_findnearest(ViewContext *vc, } } - mval_prev[0] = vc->mval[0]; - mval_prev[1] = vc->mval[1]; - /* Only one element type will be non-null. */ BLI_assert(((hit.v.ele != NULL) + (hit.e.ele != NULL) + (hit.f.ele != NULL)) <= 1); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 114f540b614..d22ae5bc804 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1323,6 +1323,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); + const float stroke_depth_offset = RNA_float_get(op->ptr, "stroke_depth_offset"); ushort local_view_bits; float loc[3], rot[3]; @@ -1454,6 +1455,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (stroke_depth_order == GP_DRAWMODE_3D) { gpd->draw_mode = GP_DRAWMODE_3D; } + md->stroke_depth_offset = stroke_depth_offset; } break; @@ -1491,9 +1493,10 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); - uiLayout *row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, !in_front); - uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !in_front); + uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); } } @@ -1532,9 +1535,18 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); RNA_def_boolean(ot->srna, "use_in_front", - false, - "In Front", + true, + "Show In Front", "Show line art grease pencil in front of everything"); + RNA_def_float(ot->srna, + "stroke_depth_offset", + 0.05f, + 0.0f, + FLT_MAX, + "Stroke Offset", + "Stroke offset for the line art modifier", + 0.0f, + 0.5f); RNA_def_boolean( ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); RNA_def_enum( @@ -1543,7 +1555,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) rna_enum_gpencil_add_stroke_depth_order_items, GP_DRAWMODE_3D, "Stroke Depth Order", - "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'"); + "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front')"); } /** \} */ @@ -2133,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); } CTX_DATA_END; @@ -2366,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); DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); @@ -3363,7 +3375,7 @@ Base *ED_object_add_duplicate( ob = basen->object; /* link own references to the newly duplicated data T26816. */ - BKE_libblock_relink_to_newid(&ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id); /* DAG_relations_tag_update(bmain); */ /* caller must do */ @@ -3469,19 +3481,6 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ -static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (!base) { - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - base = BKE_view_layer_base_find(view_layer, ob); - } - - return base; -} - static int object_add_named_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -3489,8 +3488,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Base *basen; Object *ob; - const bool duplicate = RNA_boolean_get(op->ptr, "duplicate"); - const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked"); + const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; @@ -3504,41 +3502,26 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - if (duplicate) { - basen = object_add_duplicate_internal( - bmain, - scene, - view_layer, - ob, - dupflag, - /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this - * function will only work if the object is already linked in the view layer, which is not - * the case here. So we have to do the new-ID relinking ourselves - * (#copy_object_set_idnew()). - */ - LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - } - else { - /* basen is actually not a new base in this case. */ - basen = object_add_ensure_in_view_layer(bmain, view_layer, ob); - } + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves + * (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); if (basen == NULL) { - BKE_report(op->reports, - RPT_ERROR, - duplicate ? "Object could not be duplicated" : - "Object could not be linked to the view layer"); + BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT; - int mval[2]; - if (object_add_drop_xy_get(C, op, &mval)) { - ED_object_location_from_view(C, basen->object->loc); - ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); - } - /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); @@ -3556,44 +3539,164 @@ static int object_add_named_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + Object *ob_add = basen->object; + RNA_property_float_get_array(op->ptr, prop_matrix, &ob_add->obmat[0][0]); + BKE_object_apply_mat4(ob_add, ob_add->obmat, true, true); + + DEG_id_tag_update(&ob_add->id, ID_RECALC_TRANSFORM); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + ED_object_location_from_view(C, basen->object->loc); + ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); + } + } + return OPERATOR_FINISHED; } void OBJECT_OT_add_named(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Named Object"; + ot->name = "Add Object"; ot->description = "Add named object"; ot->idname = "OBJECT_OT_add_named"; /* api callbacks */ ot->invoke = object_add_drop_xy_generic_invoke; ot->exec = object_add_named_exec; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - - prop = RNA_def_boolean( - ot->srna, - "duplicate", - true, - "Duplicate", - "Create a duplicate of the object. If not set, only ensures the object is linked into the " - "active view layer, positions and selects/activates it (deselecting others)"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_boolean(ot->srna, "linked", false, "Linked", - "Duplicate object but not object data, linking to the original data (ignored if " - "'duplicate' is false)"); + "Duplicate object but not object data, linking to the original data"); RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + object_add_drop_xy_props(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object to Mouse Operator + * \{ */ + +/** + * Alternate behavior for dropping an asset that positions the appended object(s). + */ +static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + } + else { + ob = OBACT(view_layer); + } + + if (ob == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object not found"); + return OPERATOR_CANCELLED; + } + + /* Don't transform a linked object. There's just nothing to do here in this case, so return + * #OPERATOR_FINISHED. */ + if (ID_IS_LINKED(ob)) { + return OPERATOR_FINISHED; + } + + /* Ensure the locations are updated so snap reads the evaluated active location. */ + CTX_data_ensure_evaluated_depsgraph(C); + + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + uint objects_len; + Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0}); + + float matrix[4][4]; + RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); + + float mat_src_unit[4][4]; + float mat_dst_unit[4][4]; + float final_delta[4][4]; + + normalize_m4_m4(mat_src_unit, ob->obmat); + normalize_m4_m4(mat_dst_unit, matrix); + invert_m4(mat_src_unit); + mul_m4_m4m4(final_delta, mat_dst_unit, mat_src_unit); + + ED_object_xform_array_m4(objects, objects_len, final_delta); + + MEM_freeN(objects); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + float cursor[3]; + ED_object_location_from_view(C, cursor); + ED_view3d_cursor3d_position(C, mval, false, cursor); + + /* Use the active objects location since this is the ID which the user selected to drop. + * + * This transforms all selected objects, so that dropping a single object which links in + * other objects will have their relative transformation preserved. + * For example a child/parent relationship or other objects used with a boolean modifier. + * + * The caller is responsible for ensuring the selection state gives useful results. + * Link/append does this using #FILE_AUTOSELECT. */ + ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE); + } + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Object Under Mouse"; + ot->description = "Snap selected item(s) to the mouse location"; + ot->idname = "OBJECT_OT_transform_to_mouse"; + + /* api callbacks */ + ot->invoke = object_add_drop_xy_generic_invoke; + ot->exec = object_transform_to_mouse_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + RNA_def_string(ot->srna, + "name", + NULL, + MAX_ID_NAME - 2, + "Name", + "Object name to place (when unset use the active object)"); + + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + object_add_drop_xy_props(ot); } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ea9a2de090b..fe07ecef438 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -106,6 +106,7 @@ void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); /* object_add.c */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); +void OBJECT_OT_transform_to_mouse(struct wmOperatorType *ot); void OBJECT_OT_metaball_add(struct wmOperatorType *ot); void OBJECT_OT_text_add(struct wmOperatorType *ot); void OBJECT_OT_armature_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index b3bf2c64a91..b171da42522 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -112,6 +112,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_volume_import); WM_operatortype_append(OBJECT_OT_add); WM_operatortype_append(OBJECT_OT_add_named); + WM_operatortype_append(OBJECT_OT_transform_to_mouse); WM_operatortype_append(OBJECT_OT_effector_add); WM_operatortype_append(OBJECT_OT_collection_instance_add); WM_operatortype_append(OBJECT_OT_data_instance_add); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d81143d6081..acd3f058554 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); } 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); } 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); /* 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) { @@ -2589,10 +2591,10 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, - const wmEvent *event) + const int mval[2]) { int mat_slot = 0; - Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); + Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot); if (ob == NULL) { return BLI_strdup(""); } @@ -2642,8 +2644,10 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_FINISHED; } -/* used for dropbox */ -/* assigns to object under cursor, only first material slot */ +/** + * Used for drop-box. + * Assigns to object under cursor, only first material slot. + */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { /* identifiers */ diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 66390f6f165..c7dfe911ce7 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -36,6 +36,7 @@ #include "BKE_armature.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" @@ -430,3 +431,70 @@ void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xd } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object Array + * + * Low level object transform function, transforming objects by `matrix`. + * Simple alternative to full transform logic. + * \{ */ + +static bool object_parent_in_set(GSet *objects_set, Object *ob) +{ + for (Object *parent = ob->parent; parent; parent = parent->parent) { + if (BLI_gset_lookup(objects_set, parent)) { + return true; + } + } + return false; +} + +void ED_object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4]) +{ + /* Filter out objects that have parents in `objects_set`. */ + { + GSet *objects_set = BLI_gset_ptr_new_ex(__func__, objects_len); + for (uint i = 0; i < objects_len; i++) { + BLI_gset_add(objects_set, objects[i]); + } + for (uint i = 0; i < objects_len;) { + if (object_parent_in_set(objects_set, objects[i])) { + objects[i] = objects[--objects_len]; + } + else { + i++; + } + } + BLI_gset_free(objects_set, NULL); + } + + /* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */ + bool is_translation_only; + { + float test_m4_a[4][4], test_m4_b[4][4]; + unit_m4(test_m4_a); + copy_m4_m4(test_m4_b, matrix); + zero_v3(test_m4_b[3]); + is_translation_only = equals_m4m4(test_m4_a, test_m4_b); + } + + if (is_translation_only) { + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + add_v3_v3(ob->loc, matrix[3]); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + else { + for (uint i = 0; i < objects_len; i++) { + float m4[4][4]; + Object *ob = objects[i]; + BKE_object_to_mat4(ob, m4); + mul_m4_m4m4(m4, matrix, m4); + BKE_object_apply_mat4(ob, m4, true, true); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } +} + +/** \} */ diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 3ac6dca3044..367d72b0ad7 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1235,9 +1235,15 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op) const bool use_active = RNA_boolean_get(op->ptr, "use_active"); Scene *scene = CTX_data_scene(C); Object *ob_from = ED_object_active_context(C); - ParticleSystem *psys_from = - use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : - NULL; + + ParticleSystem *psys_from = NULL; + if (use_active) { + psys_from = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data; + if (psys_from == NULL) { + /* Particle System context pointer is only valid in the Properties Editor. */ + psys_from = psys_get_current(ob_from); + } + } int changed_tot = 0; int fail = 0; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index b69a563166a..80c14371c16 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1744,7 +1744,7 @@ static void ed_default_handlers( if (flag & ED_KEYMAP_TOOL) { if (flag & ED_KEYMAP_GIZMO) { WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area); + ®ion->handlers, WM_event_get_keymap_from_toolsystem_with_gizmos, area); } else { WM_event_add_keymap_handler_dynamic( @@ -1940,7 +1940,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) rcti window_rect; WM_window_rect_calc(win, &window_rect); - /* set typedefinitions */ + /* Set type-definitions. */ area->type = BKE_spacetype_from_id(area->spacetype); if (area->type == NULL) { @@ -3027,7 +3027,7 @@ void ED_region_panels_layout_ex(const bContext *C, search_filter); } - /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */ + /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */ if (has_instanced_panel) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->type == NULL) { diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 841792d5f2d..fa0cfd16817 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -935,6 +935,10 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } } } + + /* Ensure test-motion values are never shared between regions. */ + const bool use_cycle = !WM_cursor_test_motion_and_update((const int[2]){-1, -1}); + UNUSED_VARS(use_cycle); } /* Cursors, for time being set always on edges, diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e5fbcbb0b6c..66140cba9c6 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -219,6 +219,20 @@ bool ED_operator_objectmode(bContext *C) return true; } +/** + * Same as #ED_operator_objectmode() but additionally sets a "disabled hint". That is, a message + * to be displayed to the user explaining why the operator can't be used in current context. + */ +bool ED_operator_objectmode_poll_msg(bContext *C) +{ + if (!ED_operator_objectmode(C)) { + CTX_wm_operator_poll_msg_set(C, "Only supported in object mode"); + return false; + } + + return true; +} + static bool ed_spacetype_test(bContext *C, int type) { if (ED_operator_areaactive(C)) { @@ -1283,7 +1297,7 @@ static ScrEdge *screen_area_edge_from_cursor(const bContext *C, * * callbacks: * - * invoke() gets called on shift+lmb drag in action-zone + * invoke() gets called on Shift-LMB drag in action-zone * exec() execute without any user interaction, based on properties * call init(), add handler * @@ -2078,7 +2092,7 @@ typedef struct sAreaSplitData { int bigger, smaller; /* constraints for moving new edge */ int delta; /* delta move edge */ int origmin, origsize; /* to calculate fac, for property storage */ - int previewmode; /* draw previewline, then split */ + int previewmode; /* draw preview-line, then split. */ void *draw_callback; /* call `screen_draw_split_preview` */ bool do_snap; @@ -2628,8 +2642,8 @@ static int area_max_regionsize(ScrArea *area, ARegion *scalear, AZEdge edge) dist = BLI_rcti_size_y(&area->totrct); } - /* subtractwidth of regions on opposite side - * prevents dragging regions into other opposite regions */ + /* Subtract the width of regions on opposite side + * prevents dragging regions into other opposite regions. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region == scalear) { continue; @@ -3082,12 +3096,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) float cfra = (float)(CFRA); - /* init binarytree-list for getting keyframes */ + /* Initialize binary-tree-list for getting keyframes. */ struct AnimKeylist *keylist = ED_keylist_create(); - /* seed up dummy dopesheet context with flags to perform necessary filtering */ + /* Speed up dummy dope-sheet context with flags to perform necessary filtering. */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { - /* only selected channels are included */ + /* Only selected channels are included. */ ads.filterflag |= ADS_FILTER_ONLYSEL; } @@ -4206,7 +4220,7 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Region Context Menu Operator (Header/Footer/Navbar) +/** \name Region Context Menu Operator (Header/Footer/Navigation-Bar) * \{ */ static void screen_area_menu_items(ScrArea *area, uiLayout *layout) @@ -5058,6 +5072,18 @@ static int userpref_show_exec(bContext *C, wmOperator *op) int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC; int sizey = 520 * UI_DPI_FAC; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + /* Set active section via RNA, so it can fail properly. */ + + PointerRNA pref_ptr; + RNA_pointer_create(NULL, &RNA_Preferences, &U, &pref_ptr); + PropertyRNA *active_section_prop = RNA_struct_find_property(&pref_ptr, "active_section"); + + RNA_property_enum_set(&pref_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop)); + RNA_property_update(C, &pref_ptr, active_section_prop); + } + /* changes context! */ if (WM_window_open(C, IFACE_("Blender Preferences"), @@ -5091,14 +5117,24 @@ static int userpref_show_exec(bContext *C, wmOperator *op) static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ - ot->name = "Show Preferences"; + ot->name = "Open Preferences..."; ot->description = "Edit user preferences and system settings"; ot->idname = "SCREEN_OT_userpref_show"; /* api callbacks */ ot->exec = userpref_show_exec; ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */ + + prop = RNA_def_enum(ot->srna, + "section", + rna_enum_preference_section_items, + 0, + "", + "Section to activate in the Preferences"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 4e0dcfe8e3c..fede01a614b 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -295,7 +295,7 @@ static uint vpaint_blend(const VPaint *vp, uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); - /* if no accumulate, clip color adding with colorig & orig alpha */ + /* If no accumulate, clip color adding with `color_orig` & `color_test`. */ if (!brush_use_accumulate(vp)) { uint color_test, a; char *cp, *ct, *co; diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index c305a11daf4..e6b76e05e16 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -60,6 +60,7 @@ class AssetCatalogTreeView : public ui::AbstractTreeView { SpaceFile &space_file_; friend class AssetCatalogTreeViewItem; + friend class AssetCatalogDropController; public: AssetCatalogTreeView(::AssetLibrary *library, @@ -86,25 +87,52 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { public: AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); - static bool has_droppable_item(const wmDrag &drag); - static bool drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name = ""); - void on_activate() override; void build_row(uiLayout &row) override; void build_context_menu(bContext &C, uiLayout &column) const override; - bool can_drop(const wmDrag &drag) const override; - std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const override; - bool on_drop(const wmDrag &drag) override; - bool can_rename() const override; bool rename(StringRefNull new_name) override; + + /** Add drag support for catalog items. */ + std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override; + /** Add dropping support for catalog items. */ + std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; +}; + +class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { + AssetCatalogTreeItem &catalog_item_; + + public: + explicit AssetCatalogDragController(AssetCatalogTreeItem &catalog_item); + + int get_drag_type() const override; + void *create_drag_data() const override; +}; + +class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { + AssetCatalogTreeItem &catalog_item_; + + public: + AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + + ::AssetLibrary &get_asset_library() const; + + static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint); + static bool drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name = ""); + + private: + bool drop_asset_catalog_into_catalog(const wmDrag &drag); + std::string drop_tooltip_asset_list(const wmDrag &drag) const; + std::string drop_tooltip_asset_catalog(const wmDrag &drag) const; }; /** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level @@ -118,11 +146,15 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - bool can_drop(const wmDrag &drag) const override; - std::string drop_tooltip(const bContext &C, - const wmDrag &drag, - const wmEvent &event) const override; - bool on_drop(const wmDrag &drag) override; + struct DropController : public ui::AbstractTreeViewItemDropController { + DropController(AssetCatalogTreeView &tree_view); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + }; + + std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; }; /* ---------------------------------------------------------------------- */ @@ -219,12 +251,8 @@ void AssetCatalogTreeViewItem::on_activate() void AssetCatalogTreeViewItem::build_row(uiLayout &row) { - if (catalog_item_.has_unsaved_changes()) { - uiItemL(&row, (label_ + "*").c_str(), icon); - } - else { - uiItemL(&row, label_.c_str(), icon); - } + const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_; + add_label(row, label_override); if (!is_hovered()) { return; @@ -275,31 +303,80 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) UI_menutype_draw(&C, mt, &column); } -bool AssetCatalogTreeViewItem::has_droppable_item(const wmDrag &drag) +bool AssetCatalogTreeViewItem::can_rename() const { - const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + return true; +} - /* There needs to be at least one asset from the current file. */ - LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { - if (!asset_item->is_external) { - return true; - } +bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +{ + /* Important to keep state. */ + BasicTreeViewItem::rename(new_name); + + const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>( + get_tree_view()); + ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); + return true; +} + +std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem:: + create_drop_controller() const +{ + return std::make_unique<AssetCatalogDropController>( + static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_); +} + +std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem:: + create_drag_controller() const +{ + return std::make_unique<AssetCatalogDragController>(catalog_item_); +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, + AssetCatalogTreeItem &catalog_item) + : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) +{ +} + +bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const +{ + if (drag.type == WM_DRAG_ASSET_CATALOG) { + /* Always supported. */ + return true; + } + if (drag.type == WM_DRAG_ASSET_LIST) { + return has_droppable_asset(drag, r_disabled_hint); } return false; } -bool AssetCatalogTreeViewItem::can_drop(const wmDrag &drag) const +std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const { - if (drag.type != WM_DRAG_ASSET_LIST) { - return false; + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_tooltip_asset_catalog(drag); } - return has_droppable_item(drag); + return drop_tooltip_asset_list(drag); } -std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, - const wmDrag &drag, - const wmEvent & /*event*/) const +std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const { + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + + const ::AssetLibrary *asset_library = tree_view<AssetCatalogTreeView>().asset_library_; + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(asset_library); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id); + + return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " + + IFACE_("into") + " '" + catalog_item_.get_name() + "'"; +} + +std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -312,11 +389,34 @@ std::string AssetCatalogTreeViewItem::drop_tooltip(const bContext & /*C*/, ")"; } -bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tree_view, - const wmDrag &drag, - CatalogID catalog_id, - StringRefNull simple_name) +bool AssetCatalogDropController::on_drop(const wmDrag &drag) { + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_asset_catalog_into_catalog(drag); + } + return drop_assets_into_catalog(tree_view<AssetCatalogTreeView>(), + drag, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name()); +} + +bool AssetCatalogDropController::drop_asset_catalog_into_catalog(const wmDrag &drag) +{ + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + ED_asset_catalog_move( + &get_asset_library(), catalog_drag->drag_catalog_id, catalog_item_.get_catalog_id()); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr); + return true; +} + +bool AssetCatalogDropController::drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); const ListBase *asset_drags = WM_drag_asset_list_get(&drag); if (!asset_drags) { return false; @@ -339,28 +439,46 @@ bool AssetCatalogTreeViewItem::drop_into_catalog(const AssetCatalogTreeView &tre return true; } -bool AssetCatalogTreeViewItem::on_drop(const wmDrag &drag) +bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, + const char **r_disabled_hint) { - const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>( - get_tree_view()); - return drop_into_catalog( - tree_view, drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + + *r_disabled_hint = nullptr; + /* There needs to be at least one asset from the current file. */ + LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { + if (!asset_item->is_external) { + return true; + } + } + + *r_disabled_hint = "Only assets from this current file can be moved between catalogs"; + return false; } -bool AssetCatalogTreeViewItem::can_rename() const +::AssetLibrary &AssetCatalogDropController::get_asset_library() const { - return true; + return *tree_view<AssetCatalogTreeView>().asset_library_; } -bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +/* ---------------------------------------------------------------------- */ + +AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeItem &catalog_item) + : catalog_item_(catalog_item) { - /* Important to keep state. */ - BasicTreeViewItem::rename(new_name); +} - const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>( - get_tree_view()); - ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); - return true; +int AssetCatalogDragController::get_drag_type() const +{ + return WM_DRAG_ASSET_CATALOG; +} + +void *AssetCatalogDragController::create_drag_data() const +{ + wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog), + __func__); + drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id(); + return drag_catalog; } /* ---------------------------------------------------------------------- */ @@ -382,17 +500,29 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) /* ---------------------------------------------------------------------- */ -bool AssetCatalogTreeViewUnassignedItem::can_drop(const wmDrag &drag) const +std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem:: + create_drop_controller() const +{ + return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>( + static_cast<AssetCatalogTreeView &>(get_tree_view())); +} + +AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) + : ui::AbstractTreeViewItemDropController(tree_view) +{ +} + +bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop( + const wmDrag &drag, const char **r_disabled_hint) const { if (drag.type != WM_DRAG_ASSET_LIST) { return false; } - return AssetCatalogTreeViewItem::has_droppable_item(drag); + return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint); } -std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /*C*/, - const wmDrag &drag, - const wmEvent & /*event*/) const +std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( + const wmDrag &drag) const { const ListBase *asset_drags = WM_drag_asset_list_get(&drag); const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); @@ -401,16 +531,17 @@ std::string AssetCatalogTreeViewUnassignedItem::drop_tooltip(const bContext & /* TIP_("Move asset out of any catalog"); } -bool AssetCatalogTreeViewUnassignedItem::on_drop(const wmDrag &drag) +bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag) { - const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>( - get_tree_view()); /* Assign to nil catalog ID. */ - return AssetCatalogTreeViewItem::drop_into_catalog(tree_view, drag, CatalogID{}); + return AssetCatalogDropController::drop_assets_into_catalog( + tree_view<AssetCatalogTreeView>(), drag, CatalogID{}); } } // namespace blender::ed::asset_browser +/* ---------------------------------------------------------------------- */ + namespace blender::ed::asset_browser { class AssetCatalogFilterSettings { diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 24b24eb81dd..2e2f0c146d6 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -190,6 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, preview_image, @@ -242,8 +243,9 @@ static void file_draw_string(int sx, } /** - * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on - * completion. + * \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last + * line. This is the cursor position on completion to allow drawing more text + * behind that. */ static void file_draw_string_multiline(int sx, int sy, @@ -515,6 +517,7 @@ static void file_draw_preview(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, imb, @@ -1064,7 +1067,9 @@ void file_draw_list(const bContext *C, ARegion *region) layout->curr_size = params->thumbnail_size; } -static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region) +static void file_draw_invalid_library_hint(const bContext *C, + const SpaceFile *sfile, + ARegion *region) { const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); @@ -1072,9 +1077,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path)); uchar text_col[4]; - uchar text_alert_col[4]; UI_GetThemeColor4ubv(TH_TEXT, text_col); - UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col); const View2D *v2d = ®ion->v2d; const int pad = sfile->layout->tile_border_x; @@ -1085,23 +1088,42 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion int sy = v2d->tot.ymax; { - const char *message = TIP_("Library not found"); - const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path); - char *draw_string = alloca(draw_string_str_len); - BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path); - file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy); + const char *message = TIP_("Path to asset library does not exist:"); + file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy); + + sy -= line_height; + file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col); } - /* Next line, but separate it a bit further. */ - sy -= line_height; + /* Separate a bit further. */ + sy -= line_height * 2.2f; { UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO); const char *suggestion = TIP_( - "Set up the library or edit libraries in the Preferences, File Paths section"); + "Asset Libraries are local directories that can contain .blend files with assets inside.\n" + "Manage Asset Libraries from the File Paths section in Preferences."); file_draw_string_multiline( - sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL); + sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy); + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + uiBut *but = uiDefIconTextButO(block, + UI_BTYPE_BUT, + "SCREEN_OT_userpref_show", + WM_OP_INVOKE_DEFAULT, + ICON_PREFERENCES, + NULL, + sx + UI_UNIT_X, + sy - line_height - UI_UNIT_Y * 1.2f, + UI_UNIT_X * 8, + UI_UNIT_Y, + NULL); + PointerRNA *but_opptr = UI_but_operator_ptr_get(but); + RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS); + + UI_block_end(C, block); + UI_block_draw(C, block); } } @@ -1109,7 +1131,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion * Draw a string hint if the file list is invalid. * \return true if the list is invalid and a hint was drawn. */ -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); /* Only for asset browser. */ @@ -1122,7 +1144,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } - file_draw_invalid_library_hint(sfile, region); + file_draw_invalid_library_hint(C, sfile, region); return true; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index ba08777e4e2..4be5d6d8008 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -47,7 +47,7 @@ struct View2D; void file_calc_previews(const bContext *C, ARegion *region); void file_draw_list(const bContext *C, ARegion *region); -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region); +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region); void file_draw_check_ex(bContext *C, struct ScrArea *area); void file_draw_check(bContext *C); @@ -79,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f647e1d4e4f..4eb10e65867 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1950,8 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_refresh_exec; - /* Operator works for file or asset browsing */ - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refresh Asset Library Operator + * \{ */ + +static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + ED_fileselect_clear(wm, sfile); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + + return OPERATOR_FINISHED; +} + +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset Library"; + ot->description = "Reread assets and asset catalogs from the asset library on disk"; + ot->idname = "FILE_OT_asset_library_refresh"; + + /* api callbacks */ + ot->exec = file_asset_library_refresh_exec; + ot->poll = ED_operator_asset_browsing_active; } /** \} */ diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 51d0581d6a4..0e468718a04 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -247,7 +247,7 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_refresh"); + uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh"); } uiItemS(col); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index d329a8809c7..a73fa2b9740 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -817,88 +817,85 @@ static bool is_filtered_hidden(const char *filename, return false; } -static bool is_filtered_file(FileListInternEntry *file, - const char *UNUSED(root), - FileListFilter *filter) +/** + * Apply the filter string as file path matching pattern. + * \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_relpath(const FileListInternEntry *file, const FileListFilter *filter) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); + if (filter->filter_search[0] == '\0') { + return true; + } - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if (filter->filter && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ + return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0; +} + +/** \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (is_filtered_hidden(file->relpath, filter, file)) { + return false; + } + + if (FILENAME_IS_CURRPAR(file->relpath)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if (filter->filter && (filter->flags & FLF_DO_FILTER)) { + if (file->typeflag & FILE_TYPE_DIR) { + if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { + if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { + return false; } } else { - if (!(file->typeflag & filter->filter)) { - is_filtered = false; + if (!(filter->filter & FILE_TYPE_FOLDER)) { + return false; } } } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; + else { + if (!(file->typeflag & filter->filter)) { + return false; } } } + return true; +} - return is_filtered; +/** \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file(FileListInternEntry *file, + const char *UNUSED(root), + FileListFilter *filter) +{ + return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter); } -static bool is_filtered_id_file(const FileListInternEntry *file, - const char *id_group, - const char *name, - const FileListFilter *filter) +static bool is_filtered_id_file_type(const FileListInternEntry *file, + const char *id_group, + const char *name, + const FileListFilter *filter) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } - } - } - if (is_filtered && id_group) { - if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { - is_filtered = false; - } - else { - uint64_t filter_id = groupname_to_filter_id(id_group); - if (!(filter_id & filter->filter_id)) { - is_filtered = false; - } - } + if (!is_filtered_file_type(file, filter)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { + if (id_group) { + if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { + return false; } - } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; + + uint64_t filter_id = groupname_to_filter_id(id_group); + if (!(filter_id & filter->filter_id)) { + return false; } } } - return is_filtered; + return true; } /** @@ -921,40 +918,100 @@ static void prepare_filter_asset_library(const FileList *filelist, FileListFilte file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library); } +/** + * Copy a string from source to `dest`, but prefix and suffix it with a single space. + * Assumes `dest` has at least space enough for the two spaces. + */ +static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size) +{ + BLI_assert(dest_size > 2); + const size_t source_length = BLI_strncpy_rlen(dest + 1, source, dest_size - 2); + dest[0] = ' '; + dest[source_length + 1] = ' '; + dest[source_length + 2] = '\0'; +} + +/** + * Return whether at least one tag matches the search filter. + * Tags are searched as "entire words", so instead of searching for "tag" in the + * filter string, this function searches for " tag ". Assumes the search filter + * starts and ends with a space. + * + * Here the tags on the asset are written in set notation: + * + * `asset_tag_matches_filter(" some tags ", {"some", "blue"})` -> true + * `asset_tag_matches_filter(" some tags ", {"som", "tag"})` -> false + * `asset_tag_matches_filter(" some tags ", {})` -> false + */ +static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data) +{ + LISTBASE_FOREACH (const AssetTag *, asset_tag, &asset_data->tags) { + char tag_name[MAX_NAME + 2]; /* sizeof(AssetTag::name) + 2 */ + tag_copy_with_spaces(tag_name, asset_tag->name, sizeof(tag_name)); + if (BLI_strcasestr(filter_search, tag_name) != NULL) { + return true; + } + } + return false; +} + static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) { + const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); + /* Not used yet for the asset view template. */ - if (!filter->asset_catalog_filter) { + if (filter->asset_catalog_filter && !file_is_asset_visible_in_catalog_filter_settings( + filter->asset_catalog_filter, asset_data)) { + return false; + } + + if (filter->filter_search[0] == '\0') { + /* If there is no filter text, everything matches. */ return true; } - const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); - return file_is_asset_visible_in_catalog_filter_settings(filter->asset_catalog_filter, - asset_data); + /* filter->filter_search contains "*the search text*". */ + char filter_search[66]; /* sizeof(FileListFilter::filter_search) */ + const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search); + + /* When doing a name comparison, get rid of the leading/trailing asterisks. */ + filter_search[string_length - 1] = '\0'; + if (BLI_strcasestr(file->name, filter_search + 1) != NULL) { + return true; + } + + /* Replace the asterisks with spaces, so that we can do matching on " sometag "; that way + * an artist searching for "redder" doesn't result in a match for the tag "red". */ + filter_search[string_length - 1] = ' '; + filter_search[0] = ' '; + + return asset_tag_matches_filter(filter_search, asset_data); } -static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +static bool is_filtered_lib_type(FileListInternEntry *file, + const char *root, + FileListFilter *filter) { - bool is_filtered; char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; BLI_join_dirfile(path, sizeof(path), root, file->relpath); if (BLO_library_path_explode(path, dir, &group, &name)) { - is_filtered = is_filtered_id_file(file, group, name, filter); - } - else { - is_filtered = is_filtered_file(file, root, filter); + return is_filtered_id_file_type(file, group, name, filter); } + return is_filtered_file_type(file, filter); +} - return is_filtered; +static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +{ + return is_filtered_lib_type(file, root, filter) && is_filtered_file_relpath(file, filter); } static bool is_filtered_asset_library(FileListInternEntry *file, const char *root, FileListFilter *filter) { - return is_filtered_lib(file, root, filter) && is_filtered_asset(file, filter); + return is_filtered_lib_type(file, root, filter) && is_filtered_asset(file, filter); } static bool is_filtered_main(FileListInternEntry *file, @@ -969,7 +1026,7 @@ static bool is_filtered_main_assets(FileListInternEntry *file, FileListFilter *filter) { /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ - return is_filtered_id_file(file, file->relpath, file->name, filter) && + return is_filtered_id_file_type(file, file->relpath, file->name, filter) && is_filtered_asset(file, filter); } @@ -1854,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, @@ -1953,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_file/filesel.c b/source/blender/editors/space_file/filesel.c index 6ab7e4eeecf..ce76fd65a86 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -486,6 +486,18 @@ struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) return filelist_file_get_id(file); } +void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID catalog_id) +{ + if (!ED_fileselect_is_asset_browser(sfile)) { + return; + } + + FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); + params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + params->catalog_id = catalog_id; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); +} + static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data) { ID *asset_id = (ID *)custom_data; @@ -517,14 +529,12 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def const FileDirEntry *file = filelist_file_ex(files, file_index, false); if (filelist_file_get_id(file) != asset_id) { - filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); continue; } params->active_file = file_index; filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); - - /* Keep looping to deselect the other files. */ + break; } WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); @@ -984,6 +994,8 @@ static void file_attribute_columns_init(const FileSelectParams *params, FileLayo void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) { FileSelectParams *params = ED_fileselect_get_active_params(sfile); + /* Request a slightly more compact layout for asset browsing. */ + const bool compact = ED_fileselect_is_asset_browser(sfile); FileLayout *layout = NULL; View2D *v2d = ®ion->v2d; int numfiles; @@ -1003,12 +1015,13 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) layout->textheight = textheight; if (params->display == FILE_IMGDISPLAY) { + const float pad_fac = compact ? 0.15f : 0.3f; layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; - layout->tile_border_x = 0.3f * UI_UNIT_X; - layout->tile_border_y = 0.3f * UI_UNIT_X; - layout->prv_border_x = 0.3f * UI_UNIT_X; - layout->prv_border_y = 0.3f * UI_UNIT_Y; + layout->tile_border_x = pad_fac * UI_UNIT_X; + layout->tile_border_y = pad_fac * UI_UNIT_X; + layout->prv_border_x = pad_fac * UI_UNIT_X; + layout->prv_border_y = pad_fac * UI_UNIT_Y; layout->tile_w = layout->prv_w + 2 * layout->prv_border_x; layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight; layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a875b7a2c12..b115c63a569 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -659,7 +659,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) file_highlight_set(sfile, region, event->xy[0], event->xy[1]); } - if (!file_draw_hint_if_invalid(sfile, region)) { + if (!file_draw_hint_if_invalid(C, sfile, region)) { file_draw_list(C, region); } @@ -688,6 +688,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); + WM_operatortype_append(FILE_OT_asset_library_refresh); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 4694d8652f6..bf2d20cf4c9 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -323,12 +323,19 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin { const float yheight = ymaxc - yminc; - immUniformColor3f(0.7f, 0.7f, 0.7f); - /* draw with AA'd line */ GPU_line_smooth(true); GPU_blend(GPU_BLEND_ALPHA); + /* Fully opaque line on selected strips. */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* TODO: Use theme setting. */ + immUniformColor3f(1.0f, 1.0f, 1.0f); + } + else { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + } + /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0); @@ -501,7 +508,7 @@ static void nla_draw_strip(SpaceNla *snla, /* strip is in normal track */ UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -509,9 +516,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, true, - 0.0, - 0.5, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -545,11 +550,9 @@ static void nla_draw_strip(SpaceNla *snla, /* draw strip outline * - color used here is to indicate active vs non-active */ - if (strip->flag & NLASTRIP_FLAG_ACTIVE) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT)) { /* strip should appear 'sunken', so draw a light border around it */ - color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */ - color[1] = 1.0f; - color[2] = 0.9f; + color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */ } else { /* strip should appear to stand out, so draw a dark border around it */ @@ -566,7 +569,7 @@ static void nla_draw_strip(SpaceNla *snla, } else { /* non-muted - draw solid, rounded outline */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -574,9 +577,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, false, - 0.0, - 0.0, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -661,7 +662,7 @@ static void nla_draw_strip_text(AnimData *adt, } /* set text color - if colors (see above) are light, draw black text, otherwise draw white */ - if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_TWEAKUSER)) { col[0] = col[1] = col[2] = 0; } else { @@ -805,29 +806,6 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) immRectf( pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - /* draw 'embossed' lines above and below the strip for effect */ - /* white base-lines */ - GPU_line_width(2.0f); - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.3f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* black top-lines */ - GPU_line_width(1.0f); - immUniformColor3f(0.0f, 0.0f, 0.0f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* TODO: these lines but better --^ */ - immUnbindProgram(); /* draw keyframes in the action */ diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 80d3b43bf6b..600309c2c86 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -40,6 +40,7 @@ set(INC set(SRC drawnode.cc node_add.cc + node_context_path.cc node_draw.cc node_edit.cc node_geometry_attribute_search.cc diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 8a63a1f3505..24f5decacdf 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3619,7 +3619,39 @@ static void std_node_socket_draw( break; } case SOCK_IMAGE: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + if (text[0] == '\0') { + uiTemplateID(layout, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + else { + /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */ + uiLayout *row = uiLayoutSplit(layout, 0.3f, false); + uiItemL(row, text, 0); + uiTemplateID(row, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + } + else { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + } break; } case SOCK_COLLECTION: { @@ -4297,6 +4329,25 @@ void node_draw_link_bezier(const View2D *v2d, UI_GetThemeColor4fv(th_col2, colors[2]); } + /* Highlight links connected to selected nodes. */ + const bool is_fromnode_selected = link->fromnode && link->fromnode->flag & SELECT; + const bool is_tonode_selected = link->tonode && link->tonode->flag & SELECT; + if (is_fromnode_selected || is_tonode_selected) { + float color_selected[4]; + UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected); + const float alpha = color_selected[3]; + + /* Interpolate color if highlight color is not fully transparent. */ + if (alpha != 0.0) { + if (is_fromnode_selected) { + interp_v3_v3v3(colors[1], colors[1], color_selected, alpha); + } + if (is_tonode_selected) { + interp_v3_v3v3(colors[2], colors[2], color_selected, alpha); + } + } + } + if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ nodelink_batch_add_link(snode, @@ -4370,15 +4421,6 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) else if (link->flag & NODE_LINK_MUTED) { th_col1 = th_col2 = TH_REDALERT; } - else { - /* Regular link, highlight if connected to selected node. */ - if (link->fromnode && link->fromnode->flag & SELECT) { - th_col1 = TH_EDGE_SELECT; - } - if (link->tonode && link->tonode->flag & SELECT) { - th_col2 = TH_EDGE_SELECT; - } - } } else { /* Invalid link. */ diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7b6ca5e6e61..cb66d0dbd2b 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -758,7 +758,7 @@ static bool node_add_file_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); return ED_operator_node_editable(C) && - ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY); } static int node_add_file_exec(bContext *C, wmOperator *op) @@ -784,6 +784,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op) case NTREE_COMPOSIT: type = CMP_NODE_IMAGE; break; + case NTREE_GEOMETRY: + type = GEO_NODE_IMAGE_TEXTURE; + break; default: return OPERATOR_CANCELLED; } @@ -797,7 +800,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - node->id = (ID *)ima; + if (type == GEO_NODE_IMAGE_TEXTURE) { + bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first; + bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value; + socket_value->value = ima; + } + else { + node->id = (ID *)ima; + } /* When adding new image file via drag-drop we need to load imbuf in order * to get proper image source. diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc new file mode 100644 index 00000000000..a0ff7f3ce25 --- /dev/null +++ b/source/blender/editors/space_node/node_context_path.cc @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spnode + * \brief Node breadcrumbs drawing + */ + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "BKE_context.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "UI_interface.hh" + +#include "node_intern.h" + +struct Mesh; +struct Curve; +struct Light; +struct World; +struct Material; + +namespace blender::ed::space_node { + +static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Object &object) +{ + if (object.type == OB_MESH && object.data) { + Mesh *mesh = (Mesh *)object.data; + ui::context_path_add_generic(path, RNA_Mesh, mesh); + } + if (object.type == OB_LAMP && object.data) { + Light *light = (Light *)object.data; + ui::context_path_add_generic(path, RNA_Light, light); + } + if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) { + Curve *curve = (Curve *)object.data; + ui::context_path_add_generic(path, RNA_Curve, curve); + } +} + +static void context_path_add_node_tree_and_node_groups(const SpaceNode &snode, + Vector<ui::ContextPathItem> &path, + const bool skip_base = false) +{ + Vector<const bNodeTreePath *> tree_path = snode.treepath; + for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(int(skip_base))) { + ui::context_path_add_generic(path, RNA_NodeTree, path_item->nodetree, ICON_NODETREE); + } +} + +static void get_context_path_node_shader(const bContext &C, + SpaceNode &snode, + Vector<ui::ContextPathItem> &path) +{ + if (snode.flag & SNODE_PIN) { + if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + /* Skip the base node tree here, because the world contains a node tree already. */ + context_path_add_node_tree_and_node_groups(snode, path, true); + } + else { + context_path_add_node_tree_and_node_groups(snode, path); + } + } + else { + Object *object = CTX_data_active_object(&C); + if (snode.shaderfrom == SNODE_SHADER_OBJECT && object != nullptr) { + ui::context_path_add_generic(path, RNA_Object, object); + if (!(object->matbits && object->matbits[object->actcol - 1])) { + context_path_add_object_data(path, *object); + } + Material *material = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, material); + } + else if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + } +#ifdef WITH_FREESTYLE + else if (snode.shaderfrom == SNODE_SHADER_LINESTYLE) { + ViewLayer *viewlayer = CTX_data_view_layer(&C); + FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(viewlayer); + ui::context_path_add_generic(path, RNA_ViewLayer, viewlayer); + Material *mat = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, mat); + } +#endif + context_path_add_node_tree_and_node_groups(snode, path, true); + } +} + +static void get_context_path_node_compositor(const bContext &C, + SpaceNode &snode, + Vector<ui::ContextPathItem> &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +static void get_context_path_node_geometry(const bContext &C, + SpaceNode &snode, + Vector<ui::ContextPathItem> &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Object *object = CTX_data_active_object(&C); + ui::context_path_add_generic(path, RNA_Object, object); + ModifierData *modifier = BKE_object_active_modifier(object); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C) +{ + SpaceNode *snode = CTX_wm_space_node(&C); + if (snode == nullptr) { + return {}; + } + + Vector<ui::ContextPathItem> context_path; + + if (snode->edittree->type == NTREE_GEOMETRY) { + get_context_path_node_geometry(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_SHADER) { + get_context_path_node_shader(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_COMPOSIT) { + get_context_path_node_compositor(C, *snode, context_path); + } + + return context_path; +} + +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 97655080192..a6496294f96 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -71,8 +71,10 @@ #include "ED_gpencil.h" #include "ED_node.h" +#include "ED_screen.h" #include "ED_space_api.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "UI_view2d.h" @@ -728,7 +730,7 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { - node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1); + node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); } GPU_blend(GPU_BLEND_NONE); @@ -807,12 +809,10 @@ static void node_socket_outline_color_get(const bool selected, float r_outline_color[4]) { if (selected) { - UI_GetThemeColor4fv(TH_TEXT_HI, r_outline_color); - r_outline_color[3] = 0.9f; + UI_GetThemeColor4fv(TH_ACTIVE, r_outline_color); } else { - copy_v4_fl(r_outline_color, 0.0f); - r_outline_color[3] = 0.6f; + UI_GetThemeColor4fv(TH_WIRE, r_outline_color); } /* Until there is a better place for per socket color, @@ -832,11 +832,6 @@ void node_socket_color_get( RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color); - - bNode *node = (bNode *)node_ptr->data; - if (node->flag & NODE_MUTED) { - r_color[3] *= 0.25f; - } } struct SocketTooltipData { @@ -854,60 +849,7 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal const GPointer value = value_log.value(); const CPPType &type = *value.type(); - if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) { - const CPPType &base_type = field_type->field_type(); - BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer); - const GField &field = field_type->get_gfield(value.get()); - if (field.node().depends_on_input()) { - if (base_type.is<int>()) { - ss << TIP_("Integer Field"); - } - else if (base_type.is<float>()) { - ss << TIP_("Float Field"); - } - else if (base_type.is<blender::float3>()) { - ss << TIP_("Vector Field"); - } - else if (base_type.is<bool>()) { - ss << TIP_("Boolean Field"); - } - else if (base_type.is<std::string>()) { - ss << TIP_("String Field"); - } - ss << TIP_(" based on:\n"); - - /* Use vector set to deduplicate inputs. */ - VectorSet<std::reference_wrapper<const FieldInput>> field_inputs; - field.node().foreach_field_input( - [&](const FieldInput &field_input) { field_inputs.add(field_input); }); - for (const FieldInput &field_input : field_inputs) { - ss << "\u2022 " << field_input.socket_inspection_name(); - if (field_input != field_inputs.as_span().last().get()) { - ss << ".\n"; - } - } - } - else { - blender::fn::evaluate_constant_field(field, buffer); - if (base_type.is<int>()) { - ss << *(int *)buffer << TIP_(" (Integer)"); - } - else if (base_type.is<float>()) { - ss << *(float *)buffer << TIP_(" (Float)"); - } - else if (base_type.is<blender::float3>()) { - ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); - } - else if (base_type.is<bool>()) { - ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (base_type.is<std::string>()) { - ss << *(std::string *)buffer << TIP_(" (String)"); - } - base_type.destruct(buffer); - } - } - else if (type.is<Object *>()) { + if (type.is<Object *>()) { id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB); } else if (type.is<Material *>()) { @@ -924,6 +866,71 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal } } +static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log, + std::stringstream &ss) +{ + const CPPType &type = value_log.type(); + const GField &field = value_log.field(); + const Span<std::string> input_tooltips = value_log.input_tooltips(); + + if (input_tooltips.is_empty()) { + if (field) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + blender::fn::evaluate_constant_field(field, buffer); + if (type.is<int>()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (type.is<float>()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (type.is<blender::float3>()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (type.is<bool>()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (type.is<std::string>()) { + ss << *(std::string *)buffer << TIP_(" (String)"); + } + type.destruct(buffer); + } + else { + /* Constant values should always be logged. */ + BLI_assert_unreachable(); + ss << "Value has not been logged"; + } + } + else { + if (type.is<int>()) { + ss << TIP_("Integer field"); + } + else if (type.is<float>()) { + ss << TIP_("Float field"); + } + else if (type.is<blender::float3>()) { + ss << TIP_("Vector field"); + } + else if (type.is<bool>()) { + ss << TIP_("Boolean field"); + } + else if (type.is<std::string>()) { + ss << TIP_("String field"); + } + else if (type.is<blender::ColorGeometry4f>()) { + ss << TIP_("Color field"); + } + ss << TIP_(" based on:\n"); + + for (const int i : input_tooltips.index_range()) { + const blender::StringRef tooltip = input_tooltips[i]; + ss << "\u2022 " << tooltip; + if (i < input_tooltips.size() - 1) { + ss << ".\n"; + } + } + } +} + static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, std::stringstream &ss) { @@ -1015,6 +1022,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { create_inspection_string_for_generic_value(*generic_value_log, ss); } + if (const geo_log::GFieldValueLog *gfield_value_log = + dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) { + create_inspection_string_for_gfield(*gfield_value_log, ss); + } else if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { create_inspection_string_for_geometry(*geo_value_log, ss); @@ -1156,7 +1167,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 0.7f); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Single point. */ @@ -1301,7 +1312,7 @@ void node_draw_sockets(const View2D *v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 0.7f); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Set handle size. */ @@ -1595,24 +1606,13 @@ static void node_draw_basis(const bContext *C, /* Shadow. */ node_draw_shadow(snode, node, BASIS_RAD, 1.0f); + rctf *rct = &node->totr; float color[4]; int color_id = node_get_colorid(node); - if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor3fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - /* Opaque headers for regular nodes. */ - UI_GetThemeColor3fv(color_id, color); - color[3] = 1.0f; - } GPU_line_width(1.0f); - rctf *rct = &node->totr; - UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); - + /* Header. */ { const rctf rect = { rct->xmin, @@ -1620,7 +1620,19 @@ static void node_draw_basis(const bContext *C, rct->ymax - NODE_DY, rct->ymax, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + float color_header[4]; + + /* Muted nodes get a mix of the background with the node color. */ + if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color_header); + } + + UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_header); } /* Show/hide icons. */ @@ -1703,31 +1715,28 @@ static void node_draw_basis(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open/close entirely. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon. */ + const int but_size = U.widget_unit * 0.8f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - rct->ymax - NODE_DY / 2.2f - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); + + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_DOWNARROW_HLT, + rct->xmin + (NODE_MARGIN_X / 3), + rct->ymax - NODE_DY / 2.2f - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); - - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, rct->ymax - NODE_DY / 2.2f, 'v', color); } char showname[128]; @@ -1737,7 +1746,7 @@ static void node_draw_basis(const bContext *C, UI_BTYPE_LABEL, 0, showname, - (int)(rct->xmin + NODE_MARGIN_X), + (int)(rct->xmin + NODE_MARGIN_X + 0.4f), (int)(rct->ymax - NODE_DY), (short)(iconofs - rct->xmin - (18.0f * U.dpi_fac)), (short)NODE_DY, @@ -1751,49 +1760,96 @@ static void node_draw_basis(const bContext *C, UI_but_flag_enable(but, UI_BUT_INACTIVE); } + /* Wire across the node when muted/disabled. */ + if (node->flag & NODE_MUTED) { + node_draw_mute_line(v2d, snode, node); + } + /* Body. */ - if (nodeTypeUndefined(node)) { + const float outline_width = 1.0f; + { /* Use warning color to indicate undefined types. */ - UI_GetThemeColor4fv(TH_REDALERT, color); - } - else if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - } - else if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); - } - else { - UI_GetThemeColor4fv(TH_NODE, color); - } + if (nodeTypeUndefined(node)) { + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); + } + /* Muted nodes get a mix of the background with the node color. */ + else if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColor4fv(TH_NODE, color); + } - if (node->flag & NODE_MUTED) { - color[3] = 0.5f; + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } + + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } + + const rctf rect = { + rct->xmin, + rct->xmax, + rct->ymin, + rct->ymax - (NODE_DY + outline_width), + }; + + UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color); } + /* Header underline. */ { - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + float color_underline[4]; + + if (node->flag & NODE_MUTED) { + UI_GetThemeColor4fv(TH_WIRE, color_underline); + } + else { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline); + } + const rctf rect = { rct->xmin, rct->xmax, - rct->ymin, + rct->ymax - (NODE_DY + outline_width), rct->ymax - NODE_DY, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&rect, true, 0.0f, color_underline); } - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); + /* Outline. */ + { + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); - } + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline); } node_draw_sockets(v2d, C, ntree, node, true, false); @@ -1828,46 +1884,45 @@ static void node_draw_hidden(const bContext *C, float scale; UI_view2d_scale_get(v2d, &scale, nullptr); + const int color_id = node_get_colorid(node); + /* Shadow. */ node_draw_shadow(snode, node, hiddenrad, 1.0f); - /* Body. */ - float color[4]; - int color_id = node_get_colorid(node); + /* Wire across the node when muted/disabled. */ if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - UI_GetThemeColor4fv(color_id, color); + node_draw_mute_line(v2d, snode, node); } - UI_draw_roundbox_aa(rct, true, hiddenrad, color); - - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); - - UI_draw_roundbox_aa(rct, false, hiddenrad, color); - } + /* Body. */ + float color[4]; + { + if (nodeTypeUndefined(node)) { + /* Use warning color to indicate undefined types. */ + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); + } + else if (node->flag & NODE_MUTED) { + /* Muted nodes get a mix of the background with the node color. */ + UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color); + } - /* Custom color inline. */ - if (node->flag & NODE_CUSTOM_COLOR) { - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } - const rctf rect = { - rct->xmin + 1, - rct->xmax - 1, - rct->ymin + 1, - rct->ymax - 1, - }; - UI_draw_roundbox_3fv_alpha(&rect, false, hiddenrad, node->color, 1.0f); + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); + UI_draw_roundbox_4fv(rct, true, hiddenrad, color); } /* Title. */ @@ -1878,36 +1933,28 @@ static void node_draw_hidden(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open / collapse icon. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon */ + const int but_size = U.widget_unit * 1.0f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - centy - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); - UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); - UI_block_emboss_set(node->block, UI_EMBOSS); - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, centy, 'h', color); - } + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_RIGHTARROW, + rct->xmin + (NODE_MARGIN_X / 3), + centy - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); + UI_block_emboss_set(node->block, UI_EMBOSS); } char showname[128]; @@ -1927,15 +1974,44 @@ static void node_draw_hidden(const bContext *C, 0, 0, ""); + + /* Outline. */ + { + const float outline_width = 1.0f; + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; + + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; + + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&rect, false, hiddenrad, color_outline); + } + if (node->flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } /* Scale widget thing. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_blend(GPU_BLEND_ALPHA); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColorShade(color_id, -10); + immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180); float dx = 10.0f; immBegin(GPU_PRIM_LINES, 4); @@ -1946,7 +2022,7 @@ static void node_draw_hidden(const bContext *C, immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f); immEnd(); - immUniformThemeColorShade(color_id, 30); + immUniformThemeColorShadeAlpha(TH_TEXT, 0, -180); dx -= snode->runtime->aspect; immBegin(GPU_PRIM_LINES, 4); @@ -1958,6 +2034,7 @@ static void node_draw_hidden(const bContext *C, immEnd(); immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); node_draw_sockets(v2d, C, ntree, node, true, false); @@ -2142,15 +2219,34 @@ void node_draw_nodetree(const bContext *C, } } -/* Draw tree path info in lower left corner. */ -static void draw_tree_path(SpaceNode *snode) +/* Draw the breadcrumb on the bottom of the editor. */ +static void draw_tree_path(const bContext &C, ARegion ®ion) { - char info[256]; + using namespace blender; + + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(®ion); + + const rcti *rect = ED_region_visible_rect(®ion); + + const uiStyle *style = UI_style_get_dpi(); + const float padding_x = 16 * UI_DPI_FAC; + const int x = rect->xmin + padding_x; + const int y = region.winy - UI_UNIT_Y * 0.6f; + const int width = BLI_rcti_size_x(rect) - 2 * padding_x; - ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info)); + uiBlock *block = UI_block_begin(&C, ®ion, __func__, UI_EMBOSS_NONE); + uiLayout *layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y, width, 1, 0, style); - UI_FontThemeColor(BLF_default(), TH_TEXT_HI); - BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info)); + Vector<ui::ContextPathItem> context_path = ed::space_node::context_path_for_space_node(C); + ui::template_breadcrumbs(*layout, context_path); + + UI_block_layout_resolve(block, nullptr, nullptr); + UI_block_end(&C, block); + UI_block_draw(&C, block); + + GPU_matrix_pop_projection(); } static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float center[2]) @@ -2227,8 +2323,6 @@ void node_draw_space(const bContext *C, ARegion *region) snode->runtime->cursor[0] /= UI_DPI_FAC; snode->runtime->cursor[1] /= UI_DPI_FAC; - int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); - ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); /* Only set once. */ @@ -2237,6 +2331,9 @@ void node_draw_space(const bContext *C, ARegion *region) /* Nodes. */ snode_set_context(C); + const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); + UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); + /* Draw parent node trees. */ if (snode->treepath.last) { bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; @@ -2264,9 +2361,6 @@ void node_draw_space(const bContext *C, ARegion *region) if (ntree) { snode_setup_v2d(snode, region, center); - /* Grid. */ - UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); - /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, path->parent_key); @@ -2305,8 +2399,6 @@ void node_draw_space(const bContext *C, ARegion *region) } } else { - /* Default grid. */ - UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, NODE_INSTANCE_KEY_NONE); @@ -2324,8 +2416,10 @@ void node_draw_space(const bContext *C, ARegion *region) } } - /* Tree path info. */ - draw_tree_path(snode); + /* Draw context path. */ + if (snode->edittree) { + draw_tree_path(*C, *region); + } /* Scrollers. */ UI_view2d_scrollers_draw(v2d, nullptr); diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index f069038cc09..c0d50e753ff 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -332,7 +332,7 @@ extern const char *node_context_dir[]; #define NODE_SOCKDY (0.1f * U.widget_unit) #define NODE_WIDTH(node) (node->width * UI_DPI_FAC) #define NODE_HEIGHT(node) (node->height * UI_DPI_FAC) -#define NODE_MARGIN_X (1.10f * U.widget_unit) +#define NODE_MARGIN_X (1.2f * U.widget_unit) #define NODE_SOCKSIZE (0.25f * U.widget_unit) #define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) #define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) @@ -341,3 +341,11 @@ extern const char *node_context_dir[]; #ifdef __cplusplus } #endif + +#ifdef __cplusplus +# include "BLI_vector.hh" +# include "UI_interface.hh" +namespace blender::ed::space_node { +Vector<ui::ContextPathItem> context_path_for_space_node(const bContext &C); +} +#endif
\ No newline at end of file diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index df4f63af20b..0c54da65e9c 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -156,6 +156,7 @@ void ED_operatormacros_node(void) OPTYPE_UNDO | OPTYPE_REGISTER); mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(mot->ptr, "remove_on_cancel", true); + RNA_boolean_set(mot->ptr, "view2d_edge_pan", true); WM_operatortype_macro_define(ot, "NODE_OT_attach"); WM_operatortype_macro_define(ot, "NODE_OT_insert_offset"); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index b69e7e98bca..76aad684b4c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -57,8 +57,12 @@ #include "BLT_translation.h" +#include "NOD_node_tree_ref.hh" + #include "node_intern.h" /* own include */ +using namespace blender::nodes::node_tree_ref_types; + /* -------------------------------------------------------------------- */ /** \name Relations Helpers * \{ */ @@ -612,160 +616,282 @@ static void snode_autoconnect(Main *bmain, /** \name Link Viewer Operator * \{ */ -static int node_link_viewer(const bContext *C, bNode *tonode) -{ - SpaceNode *snode = CTX_wm_space_node(C); +namespace blender::ed::nodes::viewer_linking { - /* context check */ - if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) { - return OPERATOR_CANCELLED; +/* Depending on the node tree type, different socket types are supported by viewer nodes. */ +static bool socket_can_be_viewed(const OutputSocketRef &socket) +{ + if (nodeSocketIsHidden(socket.bsocket())) { + return false; } - if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - return OPERATOR_CANCELLED; + if (socket.idname() == "NodeSocketVirtual") { + return false; } + if (socket.tree().btree()->type != NTREE_GEOMETRY) { + return true; + } + return ELEM(socket.typeinfo()->type, + SOCK_GEOMETRY, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_RGBA); +} - /* get viewer */ - bNode *viewer_node = nullptr; - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - if (node->flag & NODE_DO_OUTPUT) { - viewer_node = node; - break; - } - } +static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type) +{ + switch (socket_type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_RGBA: + return CD_PROP_COLOR; + default: + /* Fallback. */ + return CD_AUTO_FROM_NAME; } - /* no viewer, we make one active */ - if (viewer_node == nullptr) { - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - node->flag |= NODE_DO_OUTPUT; - viewer_node = node; - break; +} + +/** + * Find the socket to link to in a viewer node. + */ +static bNodeSocket *node_link_viewer_get_socket(bNodeTree *ntree, + bNode *viewer_node, + bNodeSocket *src_socket) +{ + if (viewer_node->type != GEO_NODE_VIEWER) { + /* In viewer nodes in the compositor, only the first input should be linked to. */ + return (bNodeSocket *)viewer_node->inputs.first; + } + /* For the geometry nodes viewer, find the socket with the correct type. */ + LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node->inputs) { + if (viewer_socket->type == src_socket->type) { + if (viewer_socket->type == SOCK_GEOMETRY) { + return viewer_socket; } + NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node->storage; + const CustomDataType data_type = socket_type_to_custom_data_type( + (eNodeSocketDatatype)src_socket->type); + BLI_assert(data_type != CD_AUTO_FROM_NAME); + storage->data_type = data_type; + nodeUpdate(ntree, viewer_node); + return viewer_socket; } } + return nullptr; +} - bNodeSocket *sock = nullptr; - bNodeLink *link = nullptr; +static bool is_viewer_node(const NodeRef &node) +{ + return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); +} - /* try to find an already connected socket to cycle to the next */ - if (viewer_node) { - link = nullptr; +static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree) +{ + Vector<const NodeRef *> viewer_nodes; + for (const NodeRef *node : tree.nodes()) { + if (is_viewer_node(*node)) { + viewer_nodes.append(node); + } + } + return viewer_nodes; +} - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->fromnode == tonode) { - if (link->tosock == viewer_node->inputs.first) { - break; - } - } +static bool is_viewer_socket_in_viewer(const InputSocketRef &socket) +{ + const NodeRef &node = socket.node(); + BLI_assert(is_viewer_node(node)); + if (node.typeinfo()->type == GEO_NODE_VIEWER) { + return true; + } + return socket.index() == 0; +} + +static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node) +{ + for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) { + if (&target_socket->node() != &viewer_node) { + continue; + } + if (!target_socket->is_available()) { + continue; + } + if (is_viewer_socket_in_viewer(*target_socket)) { + return true; } - if (link) { - /* unlink existing connection */ - sock = link->fromsock; - nodeRemLink(snode->edittree, link); + } + return false; +} - /* find a socket after the previously connected socket */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = sock->next; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } - } - } - else { - for (sock = sock->next; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } +static int get_default_viewer_type(const bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; +} + +static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node) +{ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { + if (link->tonode == &viewer_node) { + if (link->tosock->flag & SOCK_UNAVAIL) { + nodeRemLink(&btree, link); } } } +} - if (tonode) { - /* Find a selected socket that overrides the socket to connect to */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } +static const NodeRef *get_existing_viewer(const NodeTreeRef &tree) +{ + Vector<const NodeRef *> viewer_nodes = find_viewer_nodes(tree); + + /* Check if there is already an active viewer node that should be used. */ + for (const NodeRef *viewer_node : viewer_nodes) { + if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) { + return viewer_node; } - else { - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } + } + + /* If no active but non-active viewers exist, make one active. */ + if (!viewer_nodes.is_empty()) { + viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT; + return viewer_nodes[0]; + } + return nullptr; +} + +static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, + const NodeRef &node_to_view) +{ + /* Check if any of the output sockets is selected, which is the case when the user just clicked + * on the socket. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (output_socket->bsocket()->flag & SELECT) { + return output_socket; } } - /* find a socket starting from the first socket */ - if (!sock) { - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } + const OutputSocketRef *last_socket_linked_to_viewer = nullptr; + if (active_viewer_node != nullptr) { + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (!socket_can_be_viewed(*output_socket)) { + continue; } - } - else { - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } + if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { + last_socket_linked_to_viewer = output_socket; } } } - - if (sock) { - /* add a new viewer if none exists yet */ - if (!viewer_node) { - /* XXX location is a quick hack, just place it next to the linked socket */ - const int viewer_type = ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; - viewer_node = node_add_node(C, nullptr, viewer_type, sock->locx + 100, sock->locy); - if (!viewer_node) { - return OPERATOR_CANCELLED; + if (last_socket_linked_to_viewer == nullptr) { + /* If no output is connected to a viewer, use the first output that can be viewed. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (socket_can_be_viewed(*output_socket)) { + return output_socket; } - - link = nullptr; } - else { - /* get link to viewer */ - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { - break; - } + } + else { + /* Pick the next socket to be linked to the viewer. */ + const int tot_outputs = node_to_view.outputs().size(); + for (const int offset : IndexRange(1, tot_outputs - 1)) { + const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; + const OutputSocketRef &output_socket = node_to_view.output(index); + if (!socket_can_be_viewed(output_socket)) { + continue; + } + if (is_linked_to_viewer(output_socket, *active_viewer_node)) { + continue; } + return &output_socket; } + } + return nullptr; +} - if (link == nullptr) { - nodeAddLink( - snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first); - } - else { - link->fromnode = tonode; - link->fromsock = sock; - /* make sure the dependency sorting is updated */ - snode->edittree->update |= NTREE_UPDATE_LINKS; +static int link_socket_to_viewer(const bContext *C, + bNode *viewer_bnode, + bNode *bnode_to_view, + bNodeSocket *bsocket_to_view) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + if (viewer_bnode == nullptr) { + /* Create a new viewer node if none exists. */ + const int viewer_type = get_default_viewer_type(C); + viewer_bnode = node_add_node( + C, nullptr, viewer_type, bsocket_to_view->locx + 100, bsocket_to_view->locy); + if (viewer_bnode == nullptr) { + return OPERATOR_CANCELLED; } - if (ED_node_is_geometry(snode)) { - ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node); + } + + bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view); + if (viewer_bsocket == nullptr) { + return OPERATOR_CANCELLED; + } + + bNodeLink *link_to_change = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &btree->links) { + if (link->tosock == viewer_bsocket) { + link_to_change = link; + break; } + } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_update(snode, viewer_node); - DEG_id_tag_update(&snode->edittree->id, 0); + if (link_to_change == nullptr) { + nodeAddLink(btree, bnode_to_view, bsocket_to_view, viewer_bnode, viewer_bsocket); + } + else { + link_to_change->fromnode = bnode_to_view; + link_to_change->fromsock = bsocket_to_view; + btree->update |= NTREE_UPDATE_LINKS; } + remove_links_to_unavailable_viewer_sockets(*btree, *viewer_bnode); + + if (btree->type == NTREE_GEOMETRY) { + ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_bnode); + } + + ntreeUpdateTree(CTX_data_main(C), btree); + snode_update(snode, viewer_bnode); + DEG_id_tag_update(&btree->id, 0); + return OPERATOR_FINISHED; } +static int node_link_viewer(const bContext *C, bNode *bnode_to_view) +{ + if (bnode_to_view == nullptr) { + return OPERATOR_CANCELLED; + } + + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + const NodeTreeRef tree{btree}; + const NodeRef &node_to_view = *tree.find_node(*bnode_to_view); + const NodeRef *active_viewer_node = get_existing_viewer(tree); + + const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node, + node_to_view); + if (socket_to_view == nullptr) { + return OPERATOR_FINISHED; + } + + bNodeSocket *bsocket_to_view = socket_to_view->bsocket(); + bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr; + return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view); +} + +} // namespace blender::ed::nodes::viewer_linking + static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); @@ -777,7 +903,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (node_link_viewer(C, node) == OPERATOR_CANCELLED) { + if (blender::ed::nodes::viewer_linking::node_link_viewer(C, node) == OPERATOR_CANCELLED) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index bd2559c4d4d..0b5d7cdda82 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -92,9 +92,6 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) snode->id = id; snode->from = from; - snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; - snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; - ED_node_set_active_viewer_key(snode); WM_main_add_notifier(NC_SCENE | ND_NODES, NULL); @@ -204,27 +201,6 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value) } } -void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_length) -{ - int size; - - value[0] = '\0'; - int i = 0; - LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) { - if (i == 0) { - size = BLI_strncpy_rlen(value, path->display_name, max_length); - } - else { - size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name); - } - max_length -= size; - if (max_length <= 0) { - break; - } - value += size; - } -} - void ED_node_set_active_viewer_key(SpaceNode *snode) { bNodeTreePath *path = snode->treepath.last; @@ -259,6 +235,8 @@ static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(s snode->spacetype = SPACE_NODE; snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; /* backdrop */ snode->zoom = 1.0f; diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index a82f516b125..a391d032d7e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -868,7 +868,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static char *datastack_drop_tooltip(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), struct wmDropBox *UNUSED(drop)) { StackDropData *drop_data = drag->poin; @@ -1201,11 +1201,13 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *collection_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event, &data)) { + if (event && !event->shift && collection_drop_init(C, drag, event, &data)) { TreeElement *te = data.te; if (!data.from || event->ctrl) { return BLI_strdup(TIP_("Link inside Collection")); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 75bdc5dbac6..ae2b1870884 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -197,11 +197,24 @@ static void get_element_operation_type( static TreeElement *get_target_element(SpaceOutliner *space_outliner) { TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); - BLI_assert(te); return te; } +static bool outliner_operation_tree_element_poll(bContext *C) +{ + if (!ED_operator_outliner_active(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return false; + } + + return true; +} + static void unlink_action_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1426,7 +1439,7 @@ static void outliner_do_data_operation( } } -static Base *outline_batch_delete_hierarchy( +static Base *outliner_batch_delete_hierarchy( ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base) { Base *child_base, *base_next; @@ -1444,7 +1457,7 @@ static Base *outline_batch_delete_hierarchy( /* pass */ } if (parent) { - base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); + base_next = outliner_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); } } @@ -1497,7 +1510,7 @@ static void object_batch_delete_hierarchy_fn(bContext *C, ED_object_editmode_exit(C, EM_FREEDATA); } - outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); + outliner_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); } } @@ -1868,6 +1881,10 @@ static bool outliner_id_operation_item_poll(bContext *C, PropertyRNA *UNUSED(prop), const int enum_value) { + if (!outliner_operation_tree_element_poll(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); TreeElement *te = get_target_element(space_outliner); TreeStoreElem *tselem = TREESTORE(te); @@ -2254,7 +2271,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_id_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; @@ -2361,7 +2378,7 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_lib_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->prop = RNA_def_enum( ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", ""); @@ -2420,14 +2437,8 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - bAction *act; - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2482,7 +2493,7 @@ void OUTLINER_OT_action_set(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_enum_search_invoke; ot->exec = outliner_action_set_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2531,12 +2542,6 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2722,12 +2727,6 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2806,6 +2805,13 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, return DummyRNA_DEFAULT_items; } + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return DummyRNA_NULL_items; + } + + TreeStoreElem *tselem = TREESTORE(te); + static const EnumPropertyItem optype_sel_and_hide[] = { {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, @@ -2816,9 +2822,6 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, static const EnumPropertyItem optype_sel_linked[] = { {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}}; - TreeElement *te = get_target_element(space_outliner); - TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == TSE_RNA_STRUCT) { return optype_sel_linked; } @@ -2835,7 +2838,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_data_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 2a29125af19..8c70f4e3f7a 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1709,16 +1709,24 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); SEQ_prefetch_stop(scene); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbasep, scene->r.cfra, 0); + } + + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { - SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq); + SEQ_edit_flag_for_removal(scene, seqbasep, seq); } } - SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep); + SEQ_edit_remove_flagged_sequences(scene, seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index b4271ebd812..8a8a24f08ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -25,6 +25,8 @@ #include <stdlib.h> #include <string.h> +#include "MEM_guardedalloc.h" + #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -414,9 +416,17 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SEQ_ALLSEL) { action = SEL_DESELECT; break; @@ -425,6 +435,9 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } switch (action) { case SEL_SELECT: seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL); @@ -481,7 +494,15 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { seq->flag &= ~SEQ_ALLSEL; } @@ -635,11 +656,51 @@ static void sequencer_select_linked_handle(const bContext *C, } } -/* Check if click happened on image which belongs to strip. If multiple strips are found, loop - * through them in order. */ -static Sequence *seq_select_seq_from_preview(const bContext *C, - const int mval[2], - const bool center) +/** Collect sequencer that are candidates for being selected. */ +struct SeqSelect_Link { + struct SeqSelect_Link *next, *prev; + Sequence *seq; + /** Only use for center selection. */ + float center_dist_sq; +}; + +static int seq_sort_for_depth_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + + /* Exactly overlapping strips, sort by machine (so the top-most is first). */ + if (slink_a->seq->machine < slink_b->seq->machine) { + return 1; + } + if (slink_a->seq->machine > slink_b->seq->machine) { + return -1; + } + return 0; +} + +static int seq_sort_for_center_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + if (slink_a->center_dist_sq > slink_b->center_dist_sq) { + return 1; + } + if (slink_a->center_dist_sq < slink_b->center_dist_sq) { + return -1; + } + + /* Exactly overlapping strips, use depth. */ + return seq_sort_for_depth_select(a, b); +} + +/** + * Check if click happened on image which belongs to strip. + * If multiple strips are found, loop through them in order + * (depth (top-most first) or closest to mouse when `center` is true). + */ +static Sequence *seq_select_seq_from_preview( + const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); @@ -650,70 +711,82 @@ static Sequence *seq_select_seq_from_preview(const bContext *C, float mouseco_view[2]; UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); + /* Always update the coordinates (check extended after). */ + const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); /* Allow strips this far from the closest center to be included. * This allows cycling over center points which are near enough * to overlapping from the users perspective. */ - const float center_threshold_cycle_px = 5.0f; - const float center_dist_sq_eps = square_f(center_threshold_cycle_px * U.pixelsize); + const float center_dist_sq_max = square_f(75.0f * U.pixelsize); const float center_scale_px[2] = { UI_view2d_scale_get_x(v2d), UI_view2d_scale_get_y(v2d), }; - float center_co_best[2] = {0.0f}; - - if (center) { - Sequence *seq_best = NULL; - float center_dist_sq_best = 0.0f; - - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, strips) { - float co[2]; - SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); - const float center_dist_sq_test = len_squared_v2v2(co, mouseco_view); - if ((seq_best == NULL) || (center_dist_sq_test < center_dist_sq_best)) { - seq_best = seq; - center_dist_sq_best = center_dist_sq_test; - copy_v2_v2(center_co_best, co); - } - } - } + struct SeqSelect_Link *slink_active = NULL; + Sequence *seq_active = SEQ_select_active_get(scene); ListBase strips_ordered = {NULL}; Sequence *seq; SEQ_ITERATOR_FOREACH (seq, strips) { bool isect = false; + float center_dist_sq_test = 0.0f; if (center) { /* Detect overlapping center points (scaled by the zoom level). */ float co[2]; SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); - sub_v2_v2(co, center_co_best); + sub_v2_v2(co, mouseco_view); mul_v2_v2(co, center_scale_px); - isect = len_squared_v2(co) <= center_dist_sq_eps; + center_dist_sq_test = len_squared_v2(co); + isect = center_dist_sq_test <= center_dist_sq_max; + if (isect) { + /* Use an active strip penalty for "center" selection when cycle is enabled. */ + if (use_cycle && (seq == seq_active) && (seq_active->flag & SELECT)) { + center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize)); + } + } } else { isect = seq_point_image_isect(scene, seq, mouseco_view); } if (isect) { - BLI_remlink(seqbase, seq); - BLI_addtail(&strips_ordered, seq); + struct SeqSelect_Link *slink = MEM_callocN(sizeof(*slink), __func__); + slink->seq = seq; + slink->center_dist_sq = center_dist_sq_test; + BLI_addtail(&strips_ordered, slink); + + if (seq == seq_active) { + slink_active = slink; + } } } SEQ_collection_free(strips); - SEQ_sort(&strips_ordered); - Sequence *seq_active = SEQ_select_active_get(scene); - Sequence *seq_select = strips_ordered.first; - LISTBASE_FOREACH (Sequence *, seq_iter, &strips_ordered) { - if (seq_iter == seq_active && seq_iter->next != NULL) { - seq_select = seq_iter->next; - break; + BLI_listbase_sort(&strips_ordered, + center ? seq_sort_for_center_select : seq_sort_for_depth_select); + + struct SeqSelect_Link *slink_select = strips_ordered.first; + Sequence *seq_select = NULL; + if (slink_select != NULL) { + /* Only use special behavior for the active strip when it's selected. */ + if ((center == false) && slink_active && (seq_active->flag & SELECT)) { + if (use_cycle) { + if (slink_active->next) { + slink_select = slink_active->next; + } + } + else { + /* Match object selection behavior: keep the current active item unless cycle is enabled. + * Clicking again in the same location will cycle away from the active object. */ + slink_select = slink_active; + } } + seq_select = slink_select->seq; } - BLI_movelisttolist(seqbase, &strips_ordered); + BLI_freelistN(&strips_ordered); return seq_select; } @@ -759,7 +832,7 @@ static void sequencer_select_strip_impl(const Editing *ed, action = 0; } else { - if ((seq->flag & SELECT) == 0 || is_active) { + if (!((seq->flag & SELECT) && is_active)) { action = 1; } else if (toggle) { @@ -812,7 +885,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) int handle_clicked = SEQ_SIDE_NONE; Sequence *seq = NULL; if (region->regiontype == RGN_TYPE_PREVIEW) { - seq = seq_select_seq_from_preview(C, mval, center); + seq = seq_select_seq_from_preview(C, mval, toggle, extend, center); } else { seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); @@ -821,7 +894,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, * therefore both properties can be true at the same time. */ if (seq && RNA_boolean_get(op->ptr, "linked_time")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); @@ -833,7 +906,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Select left, right or overlapping the current frame. */ if (RNA_boolean_get(op->ptr, "side_of_frame")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_side_of_frame(C, v2d, mval, scene); @@ -843,7 +916,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* On Alt selection, select the strip and bordering handles. */ if (seq && RNA_boolean_get(op->ptr, "linked_handle")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_linked_handle(C, seq, handle_clicked); @@ -1694,11 +1767,17 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = { #define SEQ_CHANNEL_CHECK(_seq, _chan) (ELEM((_chan), 0, (_seq)->machine)) -static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type(ListBase *seqbasep, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) { seq->flag |= SELECT; changed = true; @@ -1708,12 +1787,18 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_basic(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_sound = SEQ_IS_SOUND(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) { seq->flag |= SELECT; changed = true; @@ -1723,12 +1808,18 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c return changed; } -static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_effect = SEQ_IS_EFFECT(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) { seq->flag |= SELECT; @@ -1739,7 +1830,10 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int return changed; } -static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_data(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const char *dir = actseq->strip ? actseq->strip->dir : NULL; @@ -1749,7 +1843,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } if (SEQ_HAS_PATH(actseq) && dir) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; @@ -1759,7 +1856,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_SCENE) { Scene *sce = actseq->scene; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) { seq->flag |= SELECT; changed = true; @@ -1768,7 +1865,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MOVIECLIP) { MovieClip *clip = actseq->clip; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip) { seq->flag |= SELECT; @@ -1778,7 +1875,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MASK) { struct Mask *mask = actseq->mask; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) { seq->flag |= SELECT; changed = true; @@ -1789,7 +1886,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; bool effects[SEQ_TYPE_MAX + 1]; @@ -1798,14 +1898,20 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann effects[i] = false; } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) { if (seq->seq1) { seq->seq1->flag |= SELECT; @@ -1823,11 +1929,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann return changed; } -static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) +static bool select_grouped_time_overlap(ListBase *seqbase, const bool is_preview, Sequence *actseq) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; @@ -1856,12 +1965,11 @@ static void query_lower_channel_strips(Sequence *seq_reference, /* Select all strips within time range and with lower channel of initial selection. Then select * effect chains of these strips. */ -static bool select_grouped_effect_link(Editing *ed, +static bool select_grouped_effect_link(ListBase *seqbase, + const bool is_preview, Sequence *UNUSED(actseq), const int UNUSED(channel)) { - ListBase *seqbase = SEQ_active_seqbase_get(ed); - /* Get collection of strips. */ SeqCollection *collection = SEQ_query_selected_strips(seqbase); const int selected_strip_count = BLI_gset_len(collection->set); @@ -1874,6 +1982,9 @@ static bool select_grouped_effect_link(Editing *ed, /* Actual logic. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } seq->flag |= SELECT; } @@ -1889,9 +2000,17 @@ static bool select_grouped_effect_link(Editing *ed, static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); Sequence *actseq = SEQ_select_active_get(scene); + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbase, scene->r.cfra, 0); + if (actseq && actseq->tmp_tag == false) { + actseq = NULL; + } + } + if (actseq == NULL) { BKE_report(op->reports, RPT_ERROR, "No active sequence!"); return OPERATOR_CANCELLED; @@ -1904,7 +2023,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) bool changed = false; if (!extend) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { seq->flag &= ~SELECT; changed = true; } @@ -1912,25 +2031,25 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) switch (type) { case SEQ_SELECT_GROUP_TYPE: - changed |= select_grouped_type(ed, actseq, channel); + changed |= select_grouped_type(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_BASIC: - changed |= select_grouped_type_basic(ed, actseq, channel); + changed |= select_grouped_type_basic(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_EFFECT: - changed |= select_grouped_type_effect(ed, actseq, channel); + changed |= select_grouped_type_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_DATA: - changed |= select_grouped_data(ed, actseq, channel); + changed |= select_grouped_data(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT: - changed |= select_grouped_effect(ed, actseq, channel); + changed |= select_grouped_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT_LINK: - changed |= select_grouped_effect_link(ed, actseq, channel); + changed |= select_grouped_effect_link(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_OVERLAP: - changed |= select_grouped_time_overlap(ed, actseq); + changed |= select_grouped_time_overlap(seqbase, is_preview, actseq); break; default: BLI_assert(0); diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 79593b0bbb0..2d2e7de7135 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -92,7 +92,14 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const Editing *ed = SEQ_editing_get(scene); - SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); + SEQ_timeline_init_boundbox(scene, &box); + MetaStack *ms = SEQ_meta_stack_active_get(ed); + /* Use meta strip range instead of scene. */ + if (ms != NULL) { + box.xmin = ms->disp_range[0] - 1; + box.xmax = ms->disp_range[1] + 1; + } + SEQ_timeline_expand_boundbox(SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index e903feeec1b..91fe1bc01b7 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -35,6 +35,7 @@ set(INC set(SRC space_spreadsheet.cc + spreadsheet_cache.cc spreadsheet_column.cc spreadsheet_context.cc spreadsheet_data_source.cc @@ -47,6 +48,7 @@ set(SRC spreadsheet_row_filter.cc spreadsheet_row_filter_ui.cc + spreadsheet_cache.hh spreadsheet_cell_value.hh spreadsheet_column.hh spreadsheet_column_values.hh diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index a82648aeee0..73e0be76466 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - MEM_SAFE_FREE(sspreadsheet->runtime); + delete sspreadsheet->runtime; LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { spreadsheet_row_filter_free(row_filter); @@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; if (sspreadsheet->runtime == nullptr) { - sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN( - sizeof(SpaceSpreadsheet_Runtime), __func__); + sspreadsheet->runtime = new SpaceSpreadsheet_Runtime(); } } @@ -138,7 +137,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) { const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); - sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); BLI_listbase_clear(&sspreadsheet_new->row_filters); LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { @@ -294,16 +293,39 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C) return {}; } -static float get_column_width(const ColumnValues &values) +static float get_default_column_width(const ColumnValues &values) { - if (values.default_width > 0) { + if (values.default_width > 0.0f) { return values.default_width; } + static const float float_width = 3; + switch (values.type()) { + case SPREADSHEET_VALUE_TYPE_BOOL: + return 2.0f; + case SPREADSHEET_VALUE_TYPE_INT32: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + return 2.0f * float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + return 3.0f * float_width; + case SPREADSHEET_VALUE_TYPE_COLOR: + return 4.0f * float_width; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + return 8.0f; + } + return float_width; +} + +static float get_column_width(const ColumnValues &values) +{ + float data_width = get_default_column_width(values); const int fontid = UI_style_get()->widget.uifont_id; BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi); const StringRefNull name = values.name(); const float name_width = BLF_width(fontid, name.data(), name.size()); - return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f); + return std::max<float>(name_width / UI_UNIT_X + 1.0f, data_width); } static float get_column_width_in_pixels(const ColumnValues &values) @@ -339,21 +361,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source) } } - data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) { - std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id); - if (values) { - if (used_ids.add(column_id)) { - SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); - SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); - BLI_addtail(&columns, new_column); - } - } - }); + data_source.foreach_default_column_ids( + [&](const SpreadsheetColumnID &column_id, const bool is_extra) { + std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id); + if (values) { + if (used_ids.add(column_id)) { + SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); + SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); + if (is_extra) { + BLI_addhead(&columns, new_column); + } + else { + BLI_addtail(&columns, new_column); + } + } + } + }); } static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + sspreadsheet->runtime->cache.set_all_unused(); spreadsheet_update_context_path(C); std::unique_ptr<DataSource> data_source = get_data_source(C); @@ -394,6 +423,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) ED_region_tag_redraw(footer); ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI); ED_region_tag_redraw(sidebar); + + /* Free all cache items that have not been used. */ + sspreadsheet->runtime->cache.remove_all_unused(); } static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc new file mode 100644 index 00000000000..2a399e018b6 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "spreadsheet_cache.hh" + +namespace blender::ed::spreadsheet { + +void SpreadsheetCache::add(std::unique_ptr<Key> key, std::unique_ptr<Value> value) +{ + key->is_used = true; + cache_map_.add_overwrite(*key, std::move(value)); + keys_.append(std::move(key)); +} + +SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key) +{ + std::unique_ptr<Value> *value = cache_map_.lookup_ptr(key); + if (value == nullptr) { + return nullptr; + } + const Key &stored_cache_key = cache_map_.lookup_key(key); + stored_cache_key.is_used = true; + return value->get(); +} + +SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add( + std::unique_ptr<Key> key, FunctionRef<std::unique_ptr<Value>()> create_value) +{ + Value *value = this->lookup(*key); + if (value != nullptr) { + return *value; + } + std::unique_ptr<Value> new_value = create_value(); + value = new_value.get(); + this->add(std::move(key), std::move(new_value)); + return *value; +} + +void SpreadsheetCache::set_all_unused() +{ + for (std::unique_ptr<Key> &key : keys_) { + key->is_used = false; + } +} + +void SpreadsheetCache::remove_all_unused() +{ + /* First remove the keys from the map and free the values. */ + for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) { + const Key &key = *it; + if (!key.is_used) { + cache_map_.remove(it); + } + } + /* Then free the keys. */ + for (int i = 0; i < keys_.size();) { + if (keys_[i]->is_used) { + i++; + } + else { + keys_.remove_and_reorder(i); + } + } +} + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh new file mode 100644 index 00000000000..d370bdab5c1 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <atomic> + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender::ed::spreadsheet { + +/** + * A generic cache for the spreadsheet. Different data sources can cache custom data using custom + * keys. + * + * Elements are removed from the cache when they are not used during a redraw. + */ +class SpreadsheetCache { + public: + class Key { + public: + virtual ~Key() = default; + + mutable bool is_used = false; + + virtual uint64_t hash() const = 0; + + friend bool operator==(const Key &a, const Key &b) + { + return a.is_equal_to(b); + } + + private: + virtual bool is_equal_to(const Key &other) const = 0; + }; + + class Value { + public: + virtual ~Value() = default; + }; + + private: + Vector<std::unique_ptr<Key>> keys_; + Map<std::reference_wrapper<const Key>, std::unique_ptr<Value>> cache_map_; + + public: + /* Adding or looking up a key tags it as being used, so that it won't be removed. */ + void add(std::unique_ptr<Key> key, std::unique_ptr<Value> value); + Value *lookup(const Key &key); + Value &lookup_or_add(std::unique_ptr<Key> key, + FunctionRef<std::unique_ptr<Value>()> create_value); + + void set_all_unused(); + void remove_all_unused(); + + template<typename T> T &lookup_or_add(std::unique_ptr<Key> key) + { + return dynamic_cast<T &>( + this->lookup_or_add(std::move(key), []() { return std::make_unique<T>(); })); + } +}; + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 68370cf6a44..877651d6530 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -97,9 +97,4 @@ std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColu return column_values; } -static constexpr float default_float_column_width = 3; -static constexpr float default_float2_column_width = 2 * default_float_column_width; -static constexpr float default_float3_column_width = 3 * default_float_column_width; -static constexpr float default_color_column_width = 4 * default_float_column_width; - } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index c38e765caee..e55a7cae6a6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -373,6 +373,21 @@ void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreads BLI_addtail(&sspreadsheet->context_path, context); } +static bScreen *find_screen_to_search_for_context(wmWindow *window, + SpaceSpreadsheet *current_space) +{ + bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) { + /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */ + ScrArea *main_area = (ScrArea *)screen->areabase.first; + SpaceLink *sl = (SpaceLink *)main_area->spacedata.first; + if (sl == (SpaceLink *)current_space) { + return main_area->full; + } + } + return screen; +} + void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet) { ED_spreadsheet_context_path_clear(sspreadsheet); @@ -385,9 +400,12 @@ void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspr if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; if (snode->edittree != nullptr) { @@ -466,9 +484,12 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet * } LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype != SPACE_NODE) { continue; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh index 2ea7fb5809f..873735c81e5 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh @@ -36,8 +36,12 @@ class DataSource { * Calls the callback with all the column ids that should be displayed as long as the user does * not manually add or remove columns. The column id can be stack allocated. Therefore, the * callback should not keep a reference to it (and copy it instead). + * + * The `is_extra` argument indicates that this column is special and should be drawn as the first + * column. (This can be made a bit more generic in the future when necessary.) */ - virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const + virtual void foreach_default_column_ids( + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { UNUSED_VARS(fn); } 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 78d9f61d8d5..c1d345d1861 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -33,18 +33,99 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field_cpp_type.hh" + #include "bmesh.h" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" namespace geo_log = blender::nodes::geometry_nodes_eval_log; +using blender::fn::GField; namespace blender::ed::spreadsheet { +static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type( + const fn::CPPType &type) +{ + if (type.is<bool>()) { + return SPREADSHEET_VALUE_TYPE_BOOL; + } + if (type.is<int>()) { + return SPREADSHEET_VALUE_TYPE_INT32; + } + if (type.is<float>()) { + return SPREADSHEET_VALUE_TYPE_FLOAT; + } + if (type.is<float2>()) { + return SPREADSHEET_VALUE_TYPE_FLOAT2; + } + if (type.is<float3>()) { + return SPREADSHEET_VALUE_TYPE_FLOAT3; + } + if (type.is<ColorGeometry4f>()) { + return SPREADSHEET_VALUE_TYPE_COLOR; + } + return std::nullopt; +} + +void ExtraColumns::foreach_default_column_ids( + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const +{ + for (const auto &item : columns_.items()) { + SpreadsheetColumnID column_id; + column_id.name = (char *)item.key.c_str(); + fn(column_id, true); + } +} + +std::unique_ptr<ColumnValues> ExtraColumns::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const fn::GSpan *values = columns_.lookup_ptr(column_id.name); + if (values == nullptr) { + return {}; + } + eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type()); + return column_values_from_function(column_type, + column_id.name, + values->size(), + [column_type, values](int index, CellValue &r_cell_value) { + const void *value = (*values)[index]; + switch (column_type) { + case SPREADSHEET_VALUE_TYPE_BOOL: + r_cell_value.value_bool = *(const bool *)value; + break; + case SPREADSHEET_VALUE_TYPE_INT32: + r_cell_value.value_int = *(const int *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT: + r_cell_value.value_float = *(const float *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + r_cell_value.value_float2 = *(const float2 *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + r_cell_value.value_float3 = *(const float3 *)value; + break; + case SPREADSHEET_VALUE_TYPE_COLOR: + r_cell_value.value_color = *( + const ColorGeometry4f *)value; + break; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + break; + } + }); +} + void GeometryDataSource::foreach_default_column_ids( - FunctionRef<void(const SpreadsheetColumnID &)> fn) const + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { + if (component_->attribute_domain_size(domain_) == 0) { + return; + } + + extra_columns_.foreach_default_column_ids(fn); component_->attribute_foreach( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { if (meta_data.domain != domain_) { @@ -55,7 +136,7 @@ void GeometryDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); - fn(column_id); + fn(column_id, false); return true; }); } @@ -63,8 +144,17 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { + if (component_->attribute_domain_size(domain_) == 0) { + return {}; + } + std::lock_guard lock{mutex_}; + std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); if (!attribute) { return {}; @@ -86,14 +176,16 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( r_cell_value.value_float = value; }); case CD_PROP_INT32: - return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - int value; - varray->get(index, &value); - r_cell_value.value_int = value; - }); + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + int value; + varray->get(index, &value); + r_cell_value.value_int = value; + }, + STREQ(column_id.name, "id") ? 5.5f : 0.0f); case CD_PROP_BOOL: return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL, column_id.name, @@ -104,40 +196,34 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT2, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float2 value; - varray->get(index, &value); - r_cell_value.value_float2 = value; - }, - default_float2_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float2 value; + varray->get(index, &value); + r_cell_value.value_float2 = value; + }); } case CD_PROP_FLOAT3: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float3 value; - varray->get(index, &value); - r_cell_value.value_float3 = value; - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float3 value; + varray->get(index, &value); + r_cell_value.value_float3 = value; + }); } case CD_PROP_COLOR: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_COLOR, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - ColorGeometry4f value; - varray->get(index, &value); - r_cell_value.value_color = value; - }, - default_color_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + ColorGeometry4f value; + varray->get(index, &value); + r_cell_value.value_color = value; + }); } default: break; @@ -293,18 +379,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included) } void InstancesDataSource::foreach_default_column_ids( - FunctionRef<void(const SpreadsheetColumnID &)> fn) const + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const { if (component_->instances_amount() == 0) { return; } + extra_columns_.foreach_default_column_ids(fn); + SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; - fn(column_id); - for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { + fn(column_id, false); + for (const char *name : {"Position", "Rotation", "Scale", "id"}) { column_id.name = (char *)name; - fn(column_id); + fn(column_id, false); } } @@ -315,6 +403,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( return {}; } + std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { Span<int> reference_handles = component_->instance_reference_handles(); @@ -346,7 +439,6 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( } } }); - values->default_width = 8.0f; return values; } Span<float4x4> transforms = component_->instance_transforms(); @@ -357,38 +449,35 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( size, [transforms](int index, CellValue &r_cell_value) { r_cell_value.value_float3 = transforms[index].translation(); - }, - default_float3_column_width); + }); } if (STREQ(column_id.name, "Rotation")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].to_euler(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].to_euler(); + }); } if (STREQ(column_id.name, "Scale")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].scale(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].scale(); + }); } Span<int> ids = component_->instance_ids(); - if (STREQ(column_id.name, "ID")) { - /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - size, - [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, - 5.5f); + if (!ids.is_empty()) { + if (STREQ(column_id.name, "id")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); + } } return {}; } @@ -469,6 +558,36 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread return geometry_set; } +static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, + Map<std::string, GField> &r_fields) +{ + if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { + return; + } + if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) { + /* No viewer is currently referenced by the context path. */ + return; + } + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context( + *sspreadsheet); + if (node_log == nullptr) { + return; + } + for (const geo_log::SocketLog &socket_log : node_log->input_logs()) { + const geo_log::ValueLog *value_log = socket_log.value(); + if (value_log == nullptr) { + continue; + } + if (const geo_log::GFieldValueLog *field_value_log = + dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) { + const GField &field = field_value_log->field(); + if (field) { + r_fields.add("Viewer", std::move(field)); + } + } + } +} + static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -481,6 +600,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec return GEO_COMPONENT_TYPE_MESH; } +class GeometryComponentCacheKey : public SpreadsheetCache::Key { + public: + /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ + const GeometryComponent *component; + + GeometryComponentCacheKey(const GeometryComponent &component) : component(&component) + { + } + + uint64_t hash() const override + { + return get_default_hash(this->component); + } + + bool is_equal_to(const Key &other) const override + { + if (const GeometryComponentCacheKey *other_geo = + dynamic_cast<const GeometryComponentCacheKey *>(&other)) { + return this->component == other_geo->component; + } + return false; + } +}; + +class GeometryComponentCacheValue : public SpreadsheetCache::Value { + public: + /* Stores the result of fields evaluated on a geometry component. Without this, fields would have + * to be reevaluated on every redraw. */ + Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays; +}; + +static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, + const GeometryComponent &component, + ExtraColumns &r_extra_columns) +{ + Map<std::string, GField> fields_to_show; + find_fields_to_evaluate(sspreadsheet, fields_to_show); + + GeometryComponentCacheValue &cache = + sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>( + std::make_unique<GeometryComponentCacheKey>(component)); + + const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; + const int domain_size = component.attribute_domain_size(domain); + for (const auto &item : fields_to_show.items()) { + StringRef name = item.key; + const GField &field = item.value; + + /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ + fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { + fn::GArray<> evaluated_array(field.cpp_type(), domain_size); + + bke::GeometryComponentFieldContext field_context{component, domain}; + fn::FieldEvaluator field_evaluator{field_context, domain_size}; + field_evaluator.add_with_destination(field, evaluated_array); + field_evaluator.evaluate(); + return evaluated_array; + }); + + r_extra_columns.add(std::move(name), evaluated_array.as_span()); + } +} + std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -493,10 +675,15 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object return {}; } + const GeometryComponent &component = *geometry_set.get_component_for_read(component_type); + ExtraColumns extra_columns; + add_fields_as_extra_columns(sspreadsheet, component, extra_columns); + if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { - return std::make_unique<InstancesDataSource>(geometry_set); + return std::make_unique<InstancesDataSource>(geometry_set, std::move(extra_columns)); } - return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain); + return std::make_unique<GeometryDataSource>( + object_eval, geometry_set, component_type, domain, std::move(extra_columns)); } } // namespace blender::ed::spreadsheet 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 d1b5dc6845e..6c88a94f585 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -28,12 +28,34 @@ struct bContext; namespace blender::ed::spreadsheet { +/** + * Contains additional named columns that should be displayed that are not stored on the geometry + * directly. This is used for displaying the evaluated fields connected to a viewer node. + */ +class ExtraColumns { + private: + /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */ + Map<std::string, fn::GSpan> columns_; + + public: + void add(std::string name, fn::GSpan data) + { + columns_.add(std::move(name), data); + } + + void foreach_default_column_ids( + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const; + + std::unique_ptr<ColumnValues> get_column_values(const SpreadsheetColumnID &column_id) const; +}; + class GeometryDataSource : public DataSource { private: Object *object_eval_; const GeometrySet geometry_set_; const GeometryComponent *component_; AttributeDomain domain_; + ExtraColumns extra_columns_; /* Some data is computed on the fly only when it is requested. Computing it does not change the * logical state of this data source. Therefore, the corresponding methods are const and need to @@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource { GeometryDataSource(Object *object_eval, GeometrySet geometry_set, const GeometryComponentType component_type, - const AttributeDomain domain) + const AttributeDomain domain, + ExtraColumns extra_columns) : object_eval_(object_eval), geometry_set_(std::move(geometry_set)), component_(geometry_set_.get_component_for_read(component_type)), - domain_(domain) + domain_(domain), + extra_columns_(std::move(extra_columns)) { } @@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource { void apply_selection_filter(MutableSpan<bool> rows_included) const; void foreach_default_column_ids( - FunctionRef<void(const SpreadsheetColumnID &)> fn) const override; + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override; std::unique_ptr<ColumnValues> get_column_values( const SpreadsheetColumnID &column_id) const override; @@ -73,16 +97,18 @@ class GeometryDataSource : public DataSource { class InstancesDataSource : public DataSource { const GeometrySet geometry_set_; const InstancesComponent *component_; + ExtraColumns extra_columns_; public: - InstancesDataSource(GeometrySet geometry_set) + InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns) : geometry_set_(std::move(geometry_set)), - component_(geometry_set_.get_component_for_read<InstancesComponent>()) + component_(geometry_set_.get_component_for_read<InstancesComponent>()), + extra_columns_(std::move(extra_columns)) { } void foreach_default_column_ids( - FunctionRef<void(const SpreadsheetColumnID &)> fn) const override; + FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override; std::unique_ptr<ColumnValues> get_column_values( const SpreadsheetColumnID &column_id) const override; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh index 8be5283fd63..8b050c2e69b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh @@ -17,12 +17,24 @@ #pragma once #include "BKE_geometry_set.hh" +#include "spreadsheet_cache.hh" -typedef struct SpaceSpreadsheet_Runtime { - int visible_rows; - int tot_rows; - int tot_columns; -} SpaceSpreadsheet_Runtime; +struct SpaceSpreadsheet_Runtime { + public: + int visible_rows = 0; + int tot_rows = 0; + int tot_columns = 0; + + blender::ed::spreadsheet::SpreadsheetCache cache; + + SpaceSpreadsheet_Runtime() = default; + + /* The cache is not copied currently. */ + SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other) + : visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns) + { + } +}; struct bContext; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 1a5eac53306..355899be279 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const int real_index = spreadsheet_layout_.row_indices[row_index]; const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values; CellValue cell_value; - column.get_value(real_index, cell_value); + if (real_index < column.size()) { + column.get_value(real_index, cell_value); + } if (cell_value.value_int.has_value()) { const int value = *cell_value.value_int; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 7999018a6b6..6acf51aec6e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -40,12 +40,14 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_lattice.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -55,6 +57,7 @@ #include "BKE_workspace.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -82,6 +85,7 @@ #endif #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "view3d_intern.h" /* own include */ @@ -515,10 +519,74 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return WM_drag_is_ID_type(drag, id_type); } +static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + return; + } + + /* Don't use the snap cursor when linking the object. Object transform isn't editable then and + * would be reset on reload. */ + if (WM_drag_asset_will_import_linked(drag)) { + return; + } + + state = drop->draw_data = ED_view3d_cursor_snap_active(); + state->draw_plane = true; + + float dimensions[3] = {0.0f}; + if (drag->type == WM_DRAG_ID) { + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); + BKE_object_dimensions_get(ob, dimensions); + } + else { + struct AssetMetaData *meta_data = WM_drag_get_asset_meta_data(drag, ID_OB); + IDProperty *dimensions_prop = BKE_asset_metadata_idprop_find(meta_data, "dimensions"); + if (dimensions_prop) { + copy_v3_v3(dimensions, IDP_Array(dimensions_prop)); + } + } + + if (!is_zero_v3(dimensions)) { + mul_v3_v3fl(state->box_dimensions, dimensions, 0.5f); + UI_GetThemeColor4ubv(TH_GIZMO_PRIMARY, state->color_box); + state->draw_box = true; + } +} + +static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag)) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = NULL; + } +} + static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); } +static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +/** + * \note the term local here refers to not being an external asset, + * poll will succeed for linked library objects. + */ +static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -532,12 +600,17 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *view3d_mat_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int xy[2], struct wmDropBox *drop) { const char *name = WM_drag_get_item_name(drag); + ARegion *region = CTX_wm_region(C); RNA_string_set(drop->ptr, "name", name); - return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); + int mval[2] = { + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, + }; + return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval); } static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -556,7 +629,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), wmDrag *UNUSED(drag), - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { return BLI_strdup(TIP_("Create object instance from object-data")); @@ -626,14 +699,85 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C), return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } -static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) +static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state, + Object *ob, + float obmat_final[4][4]) { - ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); + V3DSnapCursorData *snap_data; + snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); + copy_m4_m3(obmat_final, snap_data->plane_omat); + copy_v3_v3(obmat_final[3], snap_data->loc); + + float scale[3]; + mat4_to_size(scale, ob->obmat); + rescale_m4(obmat_final, scale); + + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float offset[3]; + BKE_boundbox_calc_center_aabb(bb, offset); + offset[2] = bb->vec[0][2]; + mul_mat3_m4_v3(obmat_final, offset); + sub_v3_v3(obmat_final[3], offset); + } +} + +static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_OB); RNA_string_set(drop->ptr, "name", id->name + 2); /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ - const bool is_imported_id = drag->type == WM_DRAG_ASSET; - RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); + BLI_assert(drag->type != WM_DRAG_ASSET); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); +} + +static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) +{ + /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, + * using auto-select to ensure the new objects are selected. + * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler) + * can use the context setup here to place the objects. */ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + + RNA_string_set(drop->ptr, "name", id->name + 2); + + Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); + if (base != NULL) { + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + V3DSnapCursorState *snap_state = drop->draw_data; + if (snap_state) { + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + } } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -698,12 +842,31 @@ static void view3d_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, - "OBJECT_OT_add_named", - view3d_ob_drop_poll, - view3d_ob_drop_copy, - WM_drag_free_imported_drag_ID, - NULL); + struct wmDropBox *drop; + drop = WM_dropbox_add(lb, + "OBJECT_OT_add_named", + view3d_ob_drop_poll_local_id, + view3d_ob_drop_copy_local_id, + WM_drag_free_imported_drag_ID, + NULL); + + 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", + view3d_ob_drop_poll_external_asset, + view3d_ob_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + NULL); + + 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", view3d_mat_drop_poll, @@ -1616,6 +1779,7 @@ static void space_view3d_refresh(const bContext *C, ScrArea *area) const char *view3d_context_dir[] = { "active_object", + "selected_ids", NULL, }; @@ -1626,8 +1790,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_dir(member)) { CTX_data_dir_set(result, view3d_context_dir); + return CTX_RESULT_OK; } - else if (CTX_data_equals(member, "active_object")) { + if (CTX_data_equals(member, "active_object")) { /* In most cases the active object is the `view_layer->basact->object`. * For the 3D view however it can be NULL when hidden. * @@ -1651,13 +1816,21 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes } } - return 1; + return CTX_RESULT_OK; } - else { - return 0; /* not found */ + if (CTX_data_equals(member, "selected_ids")) { + ListBase selected_objects; + CTX_data_selected_objects(C, &selected_objects); + LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected_objects) { + ID *selected_id = object_ptr_link->ptr.owner_id; + CTX_data_id_list_add(result, selected_id); + } + BLI_freelistN(&selected_objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; } - return -1; /* found but not available */ + return CTX_RESULT_MEMBER_NOT_FOUND; } static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 1cb650910ce..baf61befcba 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -37,6 +37,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -54,27 +55,25 @@ #include "WM_api.h" -#define STATE_LEN 3 +#define STATE_INTERN_GET(state) \ + (SnapStateIntern *)((char *)state - offsetof(SnapStateIntern, snap_state)) typedef struct SnapStateIntern { + struct SnapStateIntern *next, *prev; V3DSnapCursorState snap_state; - float prevpoint_stack[3]; - int state_active_prev; - bool is_active; } SnapStateIntern; typedef struct SnapCursorDataIntern { V3DSnapCursorState state_default; - SnapStateIntern state_intern[STATE_LEN]; + ListBase state_intern; V3DSnapCursorData snap_data; - int state_active_len; - int state_active; - struct SnapObjectContext *snap_context_v3d; const Scene *scene; short snap_elem_hidden; + float prevpoint_stack[3]; + /* Copy of the parameters of the last event state in order to detect updates. */ struct { int x; @@ -94,17 +93,6 @@ typedef struct SnapCursorDataIntern { bool is_initiated; } SnapCursorDataIntern; -static void UNUSED_FUNCTION(v3d_cursor_snap_state_init)(V3DSnapCursorState *state) -{ - state->prevpoint = NULL; - state->snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT); - state->plane_axis = 2; - rgba_uchar_args_set(state->color_point, 255, 255, 255, 255); - rgba_uchar_args_set(state->color_line, 255, 255, 255, 128); - state->draw_point = true; - state->draw_plane = false; -} static SnapCursorDataIntern g_data_intern = { .state_default = {.prevpoint = NULL, .snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | @@ -113,8 +101,9 @@ static SnapCursorDataIntern g_data_intern = { .plane_axis = 2, .color_point = {255, 255, 255, 255}, .color_line = {255, 255, 255, 128}, - .draw_point = true, - .draw_plane = false}}; + .color_box = {255, 255, 255, 128}, + .box_dimensions = {1.0f, 1.0f, 1.0f}, + .draw_point = true}}; /** * Calculate a 3x3 orientation matrix from the surface under the cursor. @@ -373,6 +362,24 @@ static void v3d_cursor_plane_draw(const RegionView3D *rv3d, } } +static void cursor_box_draw(const float dimensions[3], uchar color[4]) +{ + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4ubv(color); + imm_draw_cube_corners_3d(pos_id, (const float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f); + immUnbindProgram(); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); +} + void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, const float loc_prev[3], const float loc_curr[3], @@ -601,7 +608,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, ushort snap_elements = v3d_cursor_snap_elements(state, scene); data_intern->snap_elem_hidden = 0; - const bool draw_plane = state->draw_plane; + const bool draw_plane = state->draw_plane || state->draw_box; if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; snap_elements |= SCE_SNAP_MODE_FACE; @@ -674,6 +681,7 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, } if (draw_plane) { + RegionView3D *rv3d = region->regiondata; bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); if (orient_surface) { copy_m3_m4(omat, obmat); @@ -686,7 +694,6 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, ED_transform_calc_orientation_from_type_ex( scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); - RegionView3D *rv3d = region->regiondata; if (state->use_plane_axis_auto) { mat3_align_axis_to_v3(omat, state->plane_axis, rv3d->viewinv[2]); } @@ -699,6 +706,9 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state, orthogonalize_m3(omat, state->plane_axis); if (orient_surface) { + if (dot_v3v3(rv3d->viewinv[2], face_nor) < 0.0f) { + negate_v3(face_nor); + } v3d_cursor_poject_surface_normal(face_nor, obmat, omat); } } @@ -755,16 +765,12 @@ static bool v3d_cursor_snap_pool_fn(bContext *C) return false; } - ARegion *region = CTX_wm_region(C); - if (region->regiontype != RGN_TYPE_WINDOW) { - return false; - } - ScrArea *area = CTX_wm_area(C); if (area->spacetype != SPACE_VIEW3D) { return false; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); RegionView3D *rv3d = region->regiondata; if (rv3d->rflag & RV3D_NAVIGATING) { /* Don't draw the cursor while navigating. It can be distracting. */ @@ -781,7 +787,8 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust V3DSnapCursorData *snap_data = &data_intern->snap_data; wmWindowManager *wm = CTX_wm_manager(C); - ARegion *region = CTX_wm_region(C); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); x -= region->winrct.xmin; y -= region->winrct.ymin; if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { @@ -791,7 +798,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); } - const bool draw_plane = state->draw_plane; + const bool draw_plane = state->draw_plane || state->draw_box; if (!snap_data->snap_elem && !draw_plane) { return; } @@ -802,8 +809,6 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - GPU_blend(GPU_BLEND_ALPHA); - float matrix[4][4]; if (draw_plane) { copy_m4_m3(matrix, snap_data->plane_omat); @@ -812,7 +817,7 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix); } - if (snap_data->snap_elem && state->draw_point) { + if (snap_data->snap_elem && (state->draw_point || state->draw_box)) { const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? state->prevpoint : NULL; @@ -829,7 +834,10 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust snap_data->snap_elem); } - GPU_blend(GPU_BLEND_NONE); + if (state->draw_box) { + GPU_matrix_mul(matrix); + cursor_box_draw(state->box_dimensions, state->color_box); + } /* Restore matrix. */ wmWindowViewport(CTX_wm_window(C)); @@ -839,10 +847,11 @@ static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(cust V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void) { - if (!g_data_intern.state_active_len) { + SnapCursorDataIntern *data_intern = &g_data_intern; + if (BLI_listbase_is_empty(&data_intern->state_intern)) { return &g_data_intern.state_default; } - return (V3DSnapCursorState *)&g_data_intern.state_intern[g_data_intern.state_active]; + return &((SnapStateIntern *)data_intern->state_intern.last)->snap_state; } static void v3d_cursor_snap_activate(void) @@ -872,20 +881,16 @@ static void v3d_cursor_snap_activate(void) static void v3d_cursor_snap_free(void) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (data_intern->handle && G_MAIN->wm.first) { - WM_paint_cursor_end(data_intern->handle); + if (data_intern->handle) { + if (G_MAIN->wm.first) { + WM_paint_cursor_end(data_intern->handle); + } data_intern->handle = NULL; } if (data_intern->snap_context_v3d) { ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); data_intern->snap_context_v3d = NULL; } - - for (SnapStateIntern *state_intern = data_intern->state_intern; - state_intern < &data_intern->state_intern[STATE_LEN]; - state_intern++) { - state_intern->is_active = false; - } } void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) @@ -896,56 +901,41 @@ void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) V3DSnapCursorState *ED_view3d_cursor_snap_active(void) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (!data_intern->state_active_len) { + if (!data_intern->handle) { v3d_cursor_snap_activate(); } - data_intern->state_active_len++; - for (int i = 0; i < STATE_LEN; i++) { - SnapStateIntern *state_intern = &g_data_intern.state_intern[i]; - if (!state_intern->is_active) { - state_intern->snap_state = g_data_intern.state_default; - state_intern->is_active = true; - state_intern->state_active_prev = data_intern->state_active; - data_intern->state_active = i; - return (V3DSnapCursorState *)state_intern; - } - } + SnapStateIntern *state_intern = MEM_mallocN(sizeof(*state_intern), __func__); + state_intern->snap_state = g_data_intern.state_default; + BLI_addtail(&g_data_intern.state_intern, state_intern); - BLI_assert(false); - data_intern->state_active_len--; - return NULL; + return (V3DSnapCursorState *)&state_intern->snap_state; } void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (!data_intern->state_active_len) { - BLI_assert(false); - return; - } - - SnapStateIntern *state_intern = (SnapStateIntern *)state; - if (!state_intern->is_active) { + if (BLI_listbase_is_empty(&data_intern->state_intern)) { return; } - state_intern->is_active = false; - data_intern->state_active_len--; - if (!data_intern->state_active_len) { + SnapStateIntern *state_intern = STATE_INTERN_GET(state); + BLI_remlink(&data_intern->state_intern, state_intern); + MEM_freeN(state_intern); + if (BLI_listbase_is_empty(&data_intern->state_intern)) { v3d_cursor_snap_free(); } - else { - data_intern->state_active = state_intern->state_active_prev; - } } void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) { - SnapStateIntern *state_intern = (SnapStateIntern *)state; + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!state) { + state = ED_view3d_cursor_snap_state_get(); + } if (prev_point) { - copy_v3_v3(state_intern->prevpoint_stack, prev_point); - state->prevpoint = state_intern->prevpoint_stack; + copy_v3_v3(data_intern->prevpoint_stack, prev_point); + state->prevpoint = data_intern->prevpoint_stack; } else { state->prevpoint = NULL; @@ -958,12 +948,13 @@ V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, const int y) { SnapCursorDataIntern *data_intern = &g_data_intern; - if (C && data_intern->state_active_len) { + if (C) { wmWindowManager *wm = CTX_wm_manager(C); if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = DEG_get_input_scene(depsgraph); - ARegion *region = CTX_wm_region(C); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); View3D *v3d = CTX_wm_view3d(C); if (!state) { @@ -982,8 +973,3 @@ struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(Scene *scene) v3d_cursor_snap_context_ensure(scene); return data_intern->snap_context_v3d; } - -void ED_view3d_cursor_snap_exit(void) -{ - v3d_cursor_snap_free(); -} diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index fe347e89600..fceb6553cab 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -347,6 +347,8 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm, (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) != 0, V3D_XR_SHOW_CUSTOM_OVERLAYS); + /* Hide navigation gizmo since it gets distorted if the view matrix has a scale factor. */ + v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE; /* Reset overridden View3D data. */ v3d->lens = lens_old; diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 7fe97705765..572fc8e3156 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -742,16 +742,19 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); - ipd->snap_state = ED_view3d_cursor_snap_active(); - ipd->snap_state->draw_point = true; - ipd->snap_state->draw_plane = true; + V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active(); + if (snap_state_new) { + ipd->snap_state = snap_state = snap_state_new; + } + snap_state->draw_point = true; + snap_state->draw_plane = true; ipd->is_snap_found = view3d_interactive_add_calc_snap( C, event, ipd->co_src, ipd->matrix_orient, &ipd->use_snap, &ipd->is_snap_invert) != 0; - ipd->snap_state->draw_plane = false; - ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src); + snap_state->draw_plane = false; + ED_view3d_cursor_snap_prevpoint_set(snap_state, ipd->co_src); ipd->orient_axis = plane_axis; for (int i = 0; i < 2; i++) { @@ -1515,10 +1518,12 @@ static void preview_plane_free_fn(void *customdata) static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) { V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active(); - snap_state->draw_plane = true; + if (snap_state) { + snap_state->draw_plane = true; - gzgroup->customdata = snap_state; - gzgroup->customdata_free = preview_plane_free_fn; + gzgroup->customdata = snap_state; + gzgroup->customdata_free = preview_plane_free_fn; + } } void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 07f1f8a753c..18820039c7f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2047,19 +2047,16 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, bool enumerate, bool *r_do_nearest) { - static int last_mval[2] = {-100, -100}; bool do_nearest = false; View3D *v3d = vc->v3d; /* define if we use solid nearest select or not */ if (use_cycle) { + /* Update the coordinates (even if the return value isn't used). */ + const bool has_motion = WM_cursor_test_motion_and_update(mval); if (!XRAY_ACTIVE(v3d)) { - do_nearest = true; - if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { - do_nearest = false; - } + do_nearest = has_motion; } - copy_v2_v2_int(last_mval, mval); } else { if (!XRAY_ACTIVE(v3d)) { diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 55ec6652495..67b61ed77d8 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -615,7 +615,7 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) const float *snap_target_global = scene->cursor.location; const int pivot_point = scene->toolsettings->transform_pivot_point; - if (snap_selected_to_location(C, snap_target_global, pivot_point, use_offset, true)) { + if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) { return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index f5da7c14a88..46a664f10fa 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1730,7 +1730,12 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) { View3DShading *xr_shading = &wm->xr.session_settings.shading; /* Flags that shouldn't be overridden by the 3D View shading. */ - const int flag_copy = V3D_SHADING_WORLD_ORIENTATION; + int flag_copy = 0; + if (v3d->shading.type != + OB_SOLID) { /* Don't set V3D_SHADING_WORLD_ORIENTATION for solid shading since it results + in distorted lighting when the view matrix has a scale factor. */ + flag_copy |= V3D_SHADING_WORLD_ORIENTATION; + } BLI_assert(WM_xr_session_exists(&wm->xr)); diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index e4c20fa0be1..466c4202dbd 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -646,10 +646,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C, Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = area->spacedata.first; - Object *obedit = CTX_data_edit_object(C); RegionView3D *rv3d = region->regiondata; Base *base; - Object *ob = OBACT(view_layer); bGPdata *gpd = CTX_data_gpencil_data(C); const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); @@ -660,6 +658,15 @@ int ED_transform_calc_gizmo_stats(const bContext *C, (params->orientation_index - 1) : BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + Object *ob = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_OBACT(ob); + if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) { + Object *obpose = BKE_object_pose_armature_get(ob); + if (obpose != NULL) { + ob = obpose; + } + } + /* transform widget matrix */ unit_m4(rv3d->twmat); diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index dea8a7c6f03..c779fbe4a33 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -493,6 +493,11 @@ static void iter_snap_objects(SnapObjectContext *sctx, continue; } } + else if (snap_select == SNAP_SELECTABLE) { + if (!(base->flag & BASE_SELECTABLE)) { + continue; + } + } Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { @@ -2308,7 +2313,7 @@ static short snapMesh(SnapObjectContext *sctx, float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); + BoundBox *bb = BKE_object_boundbox_get(ob_eval); if (bb && !snap_bound_box_check_dist( bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index 2acdf5cfd9c..7bcf6812ce9 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -220,13 +220,13 @@ static void seq_snap_target_points_build(const TransInfo *t, int content_end = max_ii(seq->startdisp, seq->start + seq->len); /* Effects and single image strips produce incorrect content length. Skip these strips. */ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) { - if (seq->anim_startofs == 0 && seq->startstill == 0) { - content_start = seq->startdisp; - } - if (seq->anim_endofs == 0 && seq->endstill == 0) { - content_end = seq->enddisp; - } + content_start = seq->startdisp; + content_end = seq->enddisp; } + + CLAMP(content_start, seq->startdisp, seq->enddisp); + CLAMP(content_end, seq->startdisp, seq->enddisp); + snap_data->target_snap_points[i] = content_start; snap_data->target_snap_points[i + 1] = content_end; i += 2; diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index ed5064fdf25..5e0302130af 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -227,9 +227,19 @@ class FieldContext; * A #FieldNode that represents an input to the entire field-tree. */ class FieldInput : public FieldNode { + public: + /* The order is also used for sorting in socket inspection. */ + enum class Category { + NamedAttribute = 0, + Generated = 1, + AnonymousAttribute = 2, + Unknown, + }; + protected: const CPPType *type_; std::string debug_name_; + Category category_ = Category::Unknown; public: FieldInput(const CPPType &type, std::string debug_name = ""); @@ -245,6 +255,7 @@ class FieldInput : public FieldNode { virtual std::string socket_inspection_name() const; blender::StringRef debug_name() const; const CPPType &cpp_type() const; + Category category() const; const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef<void(const FieldInput &)> foreach_fn) const override; @@ -527,6 +538,11 @@ inline const CPPType &FieldInput::cpp_type() const return *type_; } +inline FieldInput::Category FieldInput::category() const +{ + return category_; +} + inline const CPPType &FieldInput::output_cpp_type(int output_index) const { BLI_assert(output_index == 0); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index c6125b65c5e..4de5e71c910 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -477,6 +477,12 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope, void evaluate_constant_field(const GField &field, void *r_value) { + if (field.node().depends_on_input()) { + const CPPType &type = field.cpp_type(); + type.copy_construct(type.default_value(), r_value); + return; + } + ResourceScope scope; FieldContext context; Vector<const GVArray *> varrays = evaluate_fields(scope, {field}, IndexRange(1), context); @@ -517,6 +523,7 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index") { + category_ = Category::Generated; } GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 595a0c1cc5e..9ea146c77f2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -156,7 +156,7 @@ float get_modifier_point_weight(MDeformVert *dvert, bool inverse, int def_nr) MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr); weight = dw ? dw->weight : -1.0f; if ((weight >= 0.0f) && (inverse)) { - return -1.0f; + return 1.0f - weight; } if ((weight < 0.0f) && (!inverse)) { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index ba33edd6a94..33cc3094a36 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -255,7 +255,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec BKE_gpencil_frame_active_set(depsgraph, gpd); bGPDframe *gpf = gpl->actframe; if (gpf == NULL) { - return; + continue; } apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index c5ccf1d8229..fa31aec2b5b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -160,12 +160,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec LineartCache *local_lc = gpd->runtime.lineart_cache; if (!gpd->runtime.lineart_cache) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); @@ -210,7 +212,8 @@ static void bakeModifier(Main *UNUSED(bmain), lmd->edge_types_override = lmd->edge_types; lmd->level_end_override = lmd->level_end; - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } @@ -261,7 +264,13 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } - if (ctx->scene->camera) { + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); + } + else if (ctx->scene->camera) { DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( @@ -277,6 +286,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP); walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -382,7 +392,12 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) return; } - uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiLayout *row = uiLayoutRowWithHeading(layout, false, IFACE_("Custom Camera")); + uiItemR(row, ptr, "use_custom_camera", 0, "", 0); + uiLayout *subrow = uiLayoutRow(row, true); + uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera")); + uiLayoutSetPropSep(subrow, true); + uiItemR(subrow, ptr, "source_camera", 0, "", ICON_OBJECT_DATA); uiLayout *col = uiLayoutColumn(layout, true); @@ -391,7 +406,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); - uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); + uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -412,14 +427,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); - const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + if (!show_in_front) { + uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO); + } + + layout = uiLayoutColumn(layout, false); + uiLayoutSetActive(layout, show_in_front); uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE); @@ -447,11 +471,14 @@ static bool anything_showing_through(PointerRNA *ptr) static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetActive(layout, anything_showing_through(ptr)); + uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } @@ -654,6 +681,32 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all"); } +static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayout *layout = panel->layout; + + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE); + + if (show_in_front) { + uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !show_in_front); + + uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); + uiItemR( + col, ptr, "use_offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( @@ -682,6 +735,8 @@ static void panelRegister(ARegionType *region_type) gpencil_modifier_subpanel_register( region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type); gpencil_modifier_subpanel_register( + region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index fb75b1e99ac..2e55369ea97 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -240,10 +240,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "hardness", 0, NULL, ICON_NONE); } else { - const bool is_normalized = RNA_boolean_get(ptr, "normalize_opacity"); + const bool is_normalized = RNA_boolean_get(ptr, "use_normalized_opacity"); const bool is_weighted = RNA_boolean_get(ptr, "use_weight_factor"); - uiItemR(layout, ptr, "normalize_opacity", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_normalized_opacity", 0, NULL, ICON_NONE); const char *text = (is_normalized) ? IFACE_("Strength") : IFACE_("Opacity Factor"); uiLayout *row = uiLayoutRow(layout, true); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index cac700e15f4..233992bbd31 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -191,8 +191,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "normalize_thickness", 0, NULL, ICON_NONE); - if (RNA_boolean_get(ptr, "normalize_thickness")) { + uiItemR(layout, ptr, "use_normalized_thickness", 0, NULL, ICON_NONE); + if (RNA_boolean_get(ptr, "use_normalized_thickness")) { uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE); } else { diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index c00f34185dd..d8926a63307 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -182,9 +182,9 @@ typedef struct LineartEdgeChain { typedef struct LineartEdgeChainItem { struct LineartEdgeChainItem *next, *prev; - /** Need z value for fading */ - float pos[3]; - /** For restoring position to 3d space */ + /** Need z value for fading, w value for image frame clipping. */ + float pos[4]; + /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; unsigned char line_type; @@ -299,6 +299,7 @@ typedef struct LineartRenderBuffer { bool use_loose_as_contour; bool use_loose_edge_chain; bool use_geometry_space_chain; + bool use_image_boundary_trimming; bool filter_face_mark; bool filter_face_mark_invert; @@ -311,6 +312,7 @@ typedef struct LineartRenderBuffer { bool cam_is_persp; float cam_obmat[4][4]; double camera_pos[3]; + double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ double near_clip, far_clip; float shift_x, shift_y; float crease_threshold; @@ -593,15 +595,20 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb); void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, struct LineartGpencilModifierData *lmd, - LineartCache **cached_result); + struct LineartCache **cached_result, + bool enable_stroke_offset); struct Scene; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 8935bdd1870..f3110cf87b6 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -126,7 +126,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -156,7 +156,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -177,15 +177,15 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) int last_occlusion; unsigned char last_transparency; /* Used when converting from double. */ - float use_fbcoord[2]; + float use_fbcoord[4]; float use_gpos[3]; #define VERT_COORD_TO_FLOAT(a) \ - copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \ + copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \ copy_v3fl_v3db(use_gpos, (a)->gloc); #define POS_TO_FLOAT(lpos, gpos) \ - copy_v2fl_v2db(use_fbcoord, lpos); \ + copy_v3fl_v3db(use_fbcoord, lpos); \ copy_v3fl_v3db(use_gpos, gpos); LRT_ITER_ALL_LINES_BEGIN @@ -262,6 +262,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -287,6 +288,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -340,6 +342,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); + use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -403,6 +406,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency; POS_TO_FLOAT(lpos, gpos) @@ -430,6 +434,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -926,9 +931,9 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) { - LISTBASE_FOREACH (LineartEdgeChain *, rlc, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { LineartEdgeChainItem *next_eci; - for (LineartEdgeChainItem *eci = rlc->chain.first; eci; eci = next_eci) { + for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { next_eci = eci->next; LineartEdgeChainItem *eci2, *eci3, *eci4; @@ -944,7 +949,7 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { /* And if p4 is on the extension of p1-p2 , we remove p3. */ if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { - BLI_remlink(&rlc->chain, eci3); + BLI_remlink(&ec->chain, eci3); next_eci = eci; } } @@ -952,6 +957,116 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) } } +static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb, + LineartEdgeChainItem *eci_inside, + LineartEdgeChainItem *eci_outside) +{ + float isec[2]; + float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f}; + bool found = false; + LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside; + if (eci2->pos[0] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0); + } + if (!found && eci2->pos[0] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0); + } + if (!found && eci2->pos[1] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0); + } + if (!found && eci2->pos[1] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0); + } + + if (UNLIKELY(!found)) { + return NULL; + } + + float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ? + ratiof(eci1->pos[0], eci2->pos[0], isec[0]) : + ratiof(eci1->pos[1], eci2->pos[1], isec[1]); + float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]); + + LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChainItem)); + memcpy(eci, eci1, sizeof(LineartEdgeChainItem)); + interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio); + interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio); + eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio); + eci->next = eci->prev = NULL; + return eci; +} + +#define LRT_ECI_INSIDE(eci) \ + ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \ + (eci)->pos[1] <= 1.0f) + +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) +{ + LineartEdgeChain *ec; + LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci; + bool is_inside, new_inside; + ListBase swap = {0}; + swap.first = rb->chains.first; + swap.last = rb->chains.last; + + rb->chains.last = rb->chains.first = NULL; + while ((ec = BLI_pophead(&swap)) != NULL) { + bool ec_added = false; + LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; + is_inside = LRT_ECI_INSIDE(first_eci) ? true : false; + if (!is_inside) { + ec->picked = true; + } + for (eci = first_eci->next; eci; eci = next_eci) { + next_eci = eci->next; + prev_eci = eci->prev; + + /* We only need to do something if the edge crossed from outside to the inside or from inside + * to the outside. */ + if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) { + if (new_inside == false) { + /* Stroke goes out. */ + new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci); + + LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChain)); + memcpy(new_ec, ec, sizeof(LineartEdgeChain)); + new_ec->chain.first = next_eci; + eci->prev = NULL; + prev_eci->next = NULL; + ec->chain.last = prev_eci; + BLI_addtail(&ec->chain, new_eci); + BLI_addtail(&rb->chains, ec); + ec_added = true; + ec = new_ec; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + /* Stroke comes in. */ + new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); + + ec->chain.first = eci; + eci->prev = NULL; + + BLI_addhead(&ec->chain, new_eci); + + ec_added = false; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + } + + if ((!ec_added) && is_inside) { + BLI_addtail(&rb->chains, ec); + } + } +} + /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). @@ -1008,3 +1123,45 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } } + +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera) +{ + float dir[3]; + float cam[3]; + float view[3]; + float view_clamp[3]; + copy_v3fl_v3db(cam, rb->camera_pos); + copy_v3fl_v3db(view, rb->view_vector); + + if (use_custom_camera) { + copy_v3fl_v3db(cam, rb->camera_pos); + } + else { + copy_v3fl_v3db(cam, rb->active_camera_pos); + } + + if (rb->cam_is_persp) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float orig_len = len_v3(dir); + normalize_v3(dir); + mul_v3_fl(dir, MIN2(dist, orig_len - rb->near_clip)); + add_v3_v3(eci->gpos, dir); + } + } + } + else { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float len_lim = dot_v3v3(view, dir) - rb->near_clip; + normalize_v3_v3(view_clamp, view); + mul_v3_fl(view_clamp, MIN2(dist, len_lim)); + add_v3_v3(eci->gpos, view_clamp); + } + } + } +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 6660da79b40..93e9062e910 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -2998,7 +2998,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } -static LineartCache *lineart_init_cache() +static LineartCache *lineart_init_cache(void) { LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); return lc; @@ -3016,6 +3016,8 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, LineartGpencilModifierData *lmd, + Object *camera, + Object *active_camera, LineartCache *lc) { LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); @@ -3024,10 +3026,10 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, lmd->render_buffer_ptr = rb; lc->rb_edge_types = lmd->edge_types_override; - if (!scene || !scene->camera || !lc) { + if (!scene || !camera || !lc) { return NULL; } - Camera *c = scene->camera->data; + Camera *c = camera->data; double clipping_offset = 0; if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) { @@ -3035,8 +3037,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]); - copy_m4_m4(rb->cam_obmat, scene->camera->obmat); + copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + if (active_camera) { + copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]); + } + copy_m4_m4(rb->cam_obmat, camera->obmat); rb->cam_is_persp = (c->type == CAM_PERSP); rb->near_clip = c->clip_start + clipping_offset; rb->far_clip = c->clip_end - clipping_offset; @@ -3072,6 +3077,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; + rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) != + 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; @@ -4076,11 +4083,13 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this */ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd, - LineartCache **cached_result) + LineartCache **cached_result, + bool enable_stroke_depth_offset) { LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ + Object *use_camera; double t_start; @@ -4090,14 +4099,24 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, BKE_scene_camera_switch_update(scene); - if (!scene->camera) { - return false; + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + if (!lmd->source_camera || + (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != + OB_CAMERA) { + return false; + } + } + else { + if (!scene->camera) { + return false; + } + use_camera = scene->camera; } LineartCache *lc = lineart_init_cache(); *cached_result = lc; - rb = lineart_create_render_buffer(scene, lmd, lc); + rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -4117,7 +4136,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* Get view vector before loading geometries, because we detect feature lines there. */ lineart_main_get_view_vector(rb); lineart_main_load_geometries( - depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); if (!rb->vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ @@ -4185,10 +4204,19 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); } + if (rb->use_image_boundary_trimming) { + MOD_lineart_chain_clip_at_border(rb); + } + if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { + MOD_lineart_chain_offset_towards_camera( + rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + } + /* Finally transfer the result list into cache. */ memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index 988c90483a6..b74499daf6b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob, } LineartCache *local_lc = *lc; if (!(*lc)) { - MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { - MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 018e192bf37..911c8cc2e42 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -32,7 +32,7 @@ #include "GPU_shader.h" #include "GPU_vertex_buffer.h" -#define GPU_BATCH_VBO_MAX_LEN 6 +#define GPU_BATCH_VBO_MAX_LEN 16 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag { GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), /** Has been initialized. At least one VBO is set. */ - GPU_BATCH_INIT = (1 << 16), + GPU_BATCH_INIT = (1 << 26), /** Batch is initialized but its VBOs are still being populated. (optional) */ - GPU_BATCH_BUILDING = (1 << 16), + GPU_BATCH_BUILDING = (1 << 26), /** Cached data need to be rebuild. (VAO, PSO, ...) */ - GPU_BATCH_DIRTY = (1 << 17), + GPU_BATCH_DIRTY = (1 << 27), } eGPUBatchFlag; #define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 3ea809d59a7..0d3d39839b2 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -78,8 +78,12 @@ void imm_draw_box_checker_2d_ex(float x1, int checker_size); void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2); -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]); -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]); +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]); +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]); +void imm_draw_cube_corners_3d(uint pos, + const float center[3], + const float aspect[3], + const float factor); void imm_draw_cylinder_fill_normal_3d( uint pos, uint nor, float base, float top, float height, int slices, int stacks); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index d18dc862ce7..032974db8d1 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -391,12 +391,12 @@ void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size); } -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2); @@ -412,12 +412,12 @@ void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2); @@ -428,6 +428,36 @@ void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } +void imm_draw_cube_corners_3d(uint pos, + const float center[3], + const float aspect[3], + const float factor) +{ + float coords[ARRAY_SIZE(cube_coords)][3]; + + for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); + } + + immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4); + for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { + float vec[3], co[3]; + sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]); + mul_v3_fl(vec, factor); + + copy_v3_v3(co, coords[cube_line_index[i][0]]); + immVertex3fv(pos, co); + add_v3_v3(co, vec); + immVertex3fv(pos, co); + + copy_v3_v3(co, coords[cube_line_index[i][1]]); + immVertex3fv(pos, co); + sub_v3_v3(co, vec); + immVertex3fv(pos, co); + } + immEnd(); +} + /** * Draw a cylinder. Replacement for gluCylinder. * _warning_ : Slow, better use it only if you no other choices. diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index f5bdad3e79e..bd604b90b5a 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -56,6 +56,9 @@ typedef struct AssetFilterSettings { * more than that from the file. So pointers to other IDs or ID data are strictly forbidden. */ typedef struct AssetMetaData { + /** Runtime type, to reference event callbacks. Only valid for local assets. */ + struct AssetTypeInfo *local_type_info; + /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */ struct IDProperty *properties; @@ -72,8 +75,12 @@ typedef struct AssetMetaData { * #catalog_id is updated. */ char catalog_simple_name[64]; /* MAX_NAME */ + /** Optional name of the author for display in the UI. Dynamic length. */ + char *author; + /** Optional description of this asset for display in the UI. Dynamic length. */ char *description; + /** User defined tags for this asset. The asset manager uses these for filtering, but how they * function exactly (e.g. how they are registered to provide a list of searchable available tags) * is up to the asset-engine. */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 11299ae9717..1ad884bee8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -319,6 +319,7 @@ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ .chain_smooth_tolerance = 0.2f,\ + .stroke_depth_offset = 0.05,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index c7a93080f7c..339714da255 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -978,6 +978,7 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), LRT_GPENCIL_USE_CACHE = (1 << 4), + LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilMaskSwitches { @@ -1004,6 +1005,8 @@ typedef struct LineartGpencilModifierData { short level_start; short level_end; + struct Object *source_camera; + struct Object *source_object; struct Collection *source_collection; @@ -1042,7 +1045,6 @@ typedef struct LineartGpencilModifierData { /** Strength for smoothing jagged chains. */ float chain_smooth_tolerance; - int _pad1; /* CPU mode */ float chaining_image_threshold; @@ -1053,6 +1055,9 @@ typedef struct LineartGpencilModifierData { /* #eLineArtGPencilModifierFlags, modifier internal state. */ int flags; + /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */ + float stroke_depth_offset; + /* Runtime data. */ /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index bdc9bcb6980..d4440592a00 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -49,6 +49,8 @@ typedef enum eLineartMainFlags { LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), + LRT_USE_CUSTOM_CAMERA = (1 << 17), + LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97f14b2195d..3943c063c39 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -127,6 +127,10 @@ typedef struct Mesh_Runtime { /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra; + /** Needed to ensure some thread-safety during render data pre-processing. */ + void *render_mutex; + void *_pad3; + } Mesh_Runtime; typedef struct Mesh { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index b585cbd6306..d5d2520ddf6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1581,6 +1581,16 @@ typedef struct NodeGeometrySeparateGeometry { int8_t domain; } NodeGeometrySeparateGeometry; +typedef struct NodeGeometryImageTexture { + int interpolation; + int extension; +} NodeGeometryImageTexture; + +typedef struct NodeGeometryViewer { + /* CustomDataType. */ + int8_t data_type; +} NodeGeometryViewer; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 5a88ce7c9f5..e94541fdc7f 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -501,6 +501,9 @@ enum { /* check if the object type supports materials */ #define OB_TYPE_SUPPORT_MATERIAL(_type) \ (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME)) +/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */ +#define OB_TYPE_IS_GEOMETRY(_type) \ + (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index af01bb76680..fc23d3c69a3 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -179,7 +179,9 @@ typedef struct Sequence { /** Starting and ending points of the strip in the sequence. */ int startdisp, enddisp; float sat; - float mul, handsize; + float mul; + char tmp_tag; + char _pad[3]; short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ @@ -250,7 +252,7 @@ typedef struct Sequence { /* Multiview */ char views_format; - char _pad[3]; + char _pad1[3]; struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index e24d39b61eb..841edaf8724 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -138,7 +138,14 @@ typedef struct wmWindowManager { ID id; /** Separate active from drawable. */ - struct wmWindow *windrawable, *winactive; + struct wmWindow *windrawable; + /** + * \note `CTX_wm_window(C)` is usually preferred. + * Avoid relying on this where possible as this may become NULL during when handling + * events that close or replace windows (opening a file for e.g.). + * While this happens rarely in practice, it can cause difficult to reproduce bugs. + */ + struct wmWindow *winactive; ListBase windows; /** Set on file read. */ diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 6f4f7e3e8ae..f7da912f299 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -32,8 +32,8 @@ typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; - char _pad[7]; - + float base_scale; + char _pad[3]; char base_pose_type; /* #eXRSessionBasePoseType */ /** Object to take the location and rotation as base position from. */ Object *base_pose_object; diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 979d0882dd5..5d83da170b5 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -139,6 +139,40 @@ static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr) return &asset_data->properties; } +static void rna_AssetMetaData_author_get(PointerRNA *ptr, char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + strcpy(value, asset_data->author); + } + else { + value[0] = '\0'; + } +} + +static int rna_AssetMetaData_author_length(PointerRNA *ptr) +{ + AssetMetaData *asset_data = ptr->data; + return asset_data->author ? strlen(asset_data->author) : 0; +} + +static void rna_AssetMetaData_author_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + MEM_freeN(asset_data->author); + } + + if (value[0]) { + asset_data->author = BLI_strdup(value); + } + else { + asset_data->author = NULL; + } +} + static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) { AssetMetaData *asset_data = ptr->data; @@ -347,6 +381,14 @@ static void rna_def_asset_data(BlenderRNA *brna) RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ + prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_author_get", + "rna_AssetMetaData_author_length", + "rna_AssetMetaData_author_set"); + RNA_def_property_ui_text(prop, "Author", "Name of the creator of the asset"); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index f1831bca0fe..dbf20896463 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -323,8 +323,10 @@ static void rna_AttributeGroup_next_domain(ID *id, int(skip)(CollectionPropertyIterator *iter, void *data)) { do { - CustomDataLayer *prev_layers = (CustomDataLayer *)iter->internal.array.endptr - - iter->internal.array.length; + CustomDataLayer *prev_layers = (iter->internal.array.endptr == NULL) ? + NULL : + (CustomDataLayer *)iter->internal.array.endptr - + iter->internal.array.length; CustomData *customdata = BKE_id_attributes_iterator_next_domain(id, prev_layers); if (customdata == NULL) { return; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 631d5822c5e..c4efe5a0ea1 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1309,7 +1309,7 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) prop, "Custom Curve", "Use a custom curve to define thickness change along the strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_thickness", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -1865,7 +1865,7 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_opacity", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_opacity", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Opacity", "Replace the stroke opacity"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_opacity_update"); @@ -3073,6 +3073,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CUSTOM_CAMERA); + RNA_def_property_ui_text( + prop, "Use Custom Camera", "Use custom camera instead of the active camera"); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR); RNA_def_property_ui_text(prop, @@ -3200,6 +3206,30 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "separate stroke for each overlapping type"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "stroke_depth_offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text(prop, + "Stroke Depth Offset", + "Move strokes slightly towards the camera to avoid clipping while " + "preserve depth for the viewport"); + RNA_def_property_ui_range(prop, 0.0, 0.5, 0.001, 4); + RNA_def_property_range(prop, -0.1, FLT_MAX); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + RNA_def_property_ui_text(prop, + "Offset Towards Custom Camera", + "Offset strokes towards selected camera instead of the active camera"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "source_camera", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Camera Object", "Use specified camera object for generating line art"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_lineart_source_type); RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type"); @@ -3375,6 +3405,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING); + RNA_def_property_ui_text( + prop, + "Image Boundary Trimming", + "Trim all edges right at the boundary of image(including overscan region)"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index f0508ee5317..6a36ef07dee 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2056,7 +2056,8 @@ static bool switch_type_supported(const EnumPropertyItem *item) SOCK_OBJECT, SOCK_COLLECTION, SOCK_TEXTURE, - SOCK_MATERIAL); + SOCK_MATERIAL, + SOCK_IMAGE); } static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C), @@ -5022,10 +5023,8 @@ static void def_fn_input_bool(StructRNA *srna) prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1); - RNA_def_property_ui_text( - prop, "Boolean", "Input value used for unconnected socket"); + RNA_def_property_ui_text(prop, "Boolean", "Input value used for unconnected socket"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - } static void def_fn_input_int(StructRNA *srna) @@ -5037,8 +5036,7 @@ static void def_fn_input_int(StructRNA *srna) prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "integer"); RNA_def_property_int_default(prop, 1); - RNA_def_property_ui_text( - prop, "Integer", "Input value used for unconnected socket"); + RNA_def_property_ui_text(prop, "Integer", "Input value used for unconnected socket"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -5416,6 +5414,50 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_geo_image_texture(StructRNA *srna) +{ + static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { + {SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"}, + {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"}, + {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", "Cubic interpolation"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem prop_image_extension[] = { + {SHD_IMAGE_EXTENSION_REPEAT, + "REPEAT", + 0, + "Repeat", + "Cause the image to repeat horizontally and vertically"}, + {SHD_IMAGE_EXTENSION_EXTEND, + "EXTEND", + 0, + "Extend", + "Extend by repeating edge pixels of the image"}, + {SHD_IMAGE_EXTENSION_CLIP, + "CLIP", + 0, + "Clip", + "Clip to image size and set exterior pixels as transparent"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage"); + + prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, fn_tex_prop_interpolation_items); + RNA_def_property_ui_text(prop, "Interpolation", "Method for smoothing values between pixels"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_image_extension); + RNA_def_property_ui_text( + prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_update(prop, 0, "rna_Node_update"); +} + static void def_sh_tex_gradient(StructRNA *srna) { static const EnumPropertyItem prop_gradient_type[] = { @@ -10300,12 +10342,12 @@ static void def_geo_volume_to_mesh(StructRNA *srna) {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT, "VOXEL_AMOUNT", 0, - "Voxel Amount", + "Amount", "Desired number of voxels along one axis"}, {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE, "VOXEL_SIZE", 0, - "Voxel Size", + "Size", "Desired voxel side length"}, {0, NULL, 0, NULL, NULL}, }; @@ -11035,6 +11077,20 @@ static void def_geo_separate_geometry(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_viewer(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c91ef25daa8..03976967e9f 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3074,12 +3074,34 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma PointerRNA *ptr) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data; - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; - } - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE && - !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + switch (sspreadsheet->geometry_component_type) { + case GEO_COMPONENT_TYPE_MESH: { + if (!ELEM(sspreadsheet->attribute_domain, + ATTR_DOMAIN_POINT, + ATTR_DOMAIN_EDGE, + ATTR_DOMAIN_FACE, + ATTR_DOMAIN_CORNER)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + if (!ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } } } diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 295ecf590bb..c5569683c9c 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -122,7 +122,8 @@ void RNA_api_space_filebrowser(StructRNA *srna) PropertyRNA *parm; func = RNA_def_function(srna, "activate_asset_by_id", "ED_fileselect_activate_by_id"); - RNA_def_function_ui_description(func, "Activate the asset entry that represents the given ID"); + RNA_def_function_ui_description( + func, "Activate and select the asset entry that represents the given ID"); parm = RNA_def_property(func, "id_to_activate", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(parm, "ID"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 37d2b711b7d..2514a604087 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2844,7 +2844,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "wire_select", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "edge_select"); - RNA_def_property_array(prop, 3); + RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Wire Select", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); @@ -2911,10 +2911,10 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "grid_levels", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "grid_levels"); - RNA_def_property_int_default(prop, 2); - RNA_def_property_range(prop, 0, 2); + RNA_def_property_int_default(prop, 7); + RNA_def_property_range(prop, 0, 9); RNA_def_property_ui_text( - prop, "Grid Levels", "Amount of grid lines displayed in the background"); + prop, "Grid Levels", "Number of subdivisions for the dot grid displayed in the background"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "dash_alpha", PROP_FLOAT, PROP_FACTOR); diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index 826e6d21c36..68f11d71de2 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -128,6 +128,7 @@ static void rna_def_lighting(BlenderRNA *brna) prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "aodist"); + RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text( prop, "Distance", "Length of rays, defines how far away other faces give occlusion effect"); RNA_def_property_update(prop, 0, "rna_World_update"); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 3705284ca66..dd4cbeac174 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -892,6 +892,71 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * # endif } +static void rna_XrSessionState_nav_location_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_get(xr, r_values); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_nav_location_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static void rna_XrSessionState_nav_rotation_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_get(xr, r_values); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_nav_rotation_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static float rna_XrSessionState_nav_scale_get(PointerRNA *ptr) +{ + float value; +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_get(xr, &value); +# else + UNUSED_VARS(ptr); + value = 1.0f; +# endif + return value; +} + +static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_set(xr, value); +# else + UNUSED_VARS(ptr, value); +# endif +} + static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -1615,6 +1680,13 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "base_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Base Scale", "Uniform scale to apply to VR view"); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR); RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid"); @@ -1675,7 +1747,9 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "rna_XrSessionSettings_use_absolute_tracking_get", "rna_XrSessionSettings_use_absolute_tracking_set"); RNA_def_property_ui_text( - prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime"); + prop, + "Absolute Tracking", + "Allow the VR tracking origin to be defined independently of the headset location"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } @@ -1981,6 +2055,32 @@ static void rna_def_xr_session_state(BlenderRNA *brna) "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + prop = RNA_def_property(srna, "navigation_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_location_get", "rna_XrSessionState_nav_location_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Location", + "Location offset to apply to base pose when determining viewer location"); + + prop = RNA_def_property(srna, "navigation_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_rotation_get", "rna_XrSessionState_nav_rotation_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Rotation", + "Rotation offset to apply to base pose when determining viewer rotation"); + + prop = RNA_def_property(srna, "navigation_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_scale_get", "rna_XrSessionState_nav_scale_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Scale", + "Additional scale multiplier to apply to base scale when determining viewer scale"); + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, "rna_XrSessionState_actionmaps_begin", diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index a14d582063a..e087b8411f8 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_task.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -112,6 +113,31 @@ static void requiredDataMask(Object *UNUSED(ob), } } +typedef struct FluidIsolationData { + Depsgraph *depsgraph; + Object *object; + Mesh *mesh; + FluidModifierData *fmd; + + Mesh *result; +} FluidIsolationData; + +#ifdef WITH_FLUID +static void fluid_modifier_do_isolated(void *userdata) +{ + FluidIsolationData *isolation_data = (FluidIsolationData *)userdata; + + Scene *scene = DEG_get_evaluated_scene(isolation_data->depsgraph); + + Mesh *result = BKE_fluid_modifier_do(isolation_data->fmd, + isolation_data->depsgraph, + scene, + isolation_data->object, + isolation_data->mesh); + isolation_data->result = result ? result : isolation_data->mesh; +} +#endif /* WITH_FLUID */ + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *me) { #ifndef WITH_FLUID @@ -119,16 +145,24 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return me; #else FluidModifierData *fmd = (FluidModifierData *)md; - Mesh *result = NULL; if (ctx->flag & MOD_APPLY_ORCO) { return me; } - Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - - result = BKE_fluid_modifier_do(fmd, ctx->depsgraph, scene, ctx->object, me); - return result ? result : me; + /* Isolate execution of Mantaflow when running from dependency graph. The reason for this is + * because Mantaflow uses TBB to parallel its own computation which without isolation will start + * stealing tasks from dependency graph. Stealing tasks from the dependency graph might cause + * a recursive lock when Python drivers are used (because Mantaflow is interfaced via Python as + * well. */ + FluidIsolationData isolation_data; + isolation_data.depsgraph = ctx->depsgraph; + isolation_data.object = ctx->object; + isolation_data.mesh = me; + isolation_data.fmd = fmd; + BLI_task_isolate(fluid_modifier_do_isolated, &isolation_data); + + return isolation_data.result; #endif /* WITH_FLUID */ } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index e6cc7663c58..c88940c00c2 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -917,6 +917,10 @@ static void store_output_value_in_geometry(GeometrySet &geometry_set, CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); store_field_on_geometry_component(component, attribute_name, domain, field); } + if (geometry_set.has_instances()) { + InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } } /** @@ -1424,13 +1428,18 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, true); + bool has_output_attribute = false; if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { if (socket_type_has_attribute_toggle(*socket)) { + has_output_attribute = true; draw_property_for_output_socket(layout, *nmd, ptr, *socket); } } } + if (!has_output_attribute) { + uiItemL(layout, IFACE_("No group output attributes connected."), ICON_INFO); + } } static void panelRegister(ARegionType *region_type) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 69132f6c177..a312872f5d9 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1455,6 +1455,7 @@ class GeometryNodesEvaluator { } void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); this->convert_value(type, required_type, buffer, converted_buffer); + type.destruct(buffer); return {required_type, converted_buffer}; } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 7a728b9041b..f4ca9f51b1b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -193,6 +193,8 @@ set(SRC geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc geometry/nodes/legacy/node_geo_subdivision_surface.cc + geometry/nodes/legacy/node_geo_volume_to_mesh.cc + geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_statistic.cc @@ -226,11 +228,13 @@ set(SRC geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc + geometry/nodes/node_geo_image_texture.cc geometry/nodes/node_geo_input_curve_handles.cc geometry/nodes/node_geo_input_curve_tilt.cc + geometry/nodes/node_geo_input_id.cc geometry/nodes/node_geo_input_index.cc - geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_material_index.cc + geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc geometry/nodes/node_geo_input_radius.cc @@ -269,8 +273,9 @@ set(SRC geometry/nodes/node_geo_set_curve_handles.cc geometry/nodes/node_geo_set_curve_radius.cc geometry/nodes/node_geo_set_curve_tilt.cc - geometry/nodes/node_geo_set_material.cc + geometry/nodes/node_geo_set_id.cc geometry/nodes/node_geo_set_material_index.cc + geometry/nodes/node_geo_set_material.cc geometry/nodes/node_geo_set_point_radius.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_set_shade_smooth.cc @@ -445,6 +450,7 @@ set(SRC NOD_shader.h NOD_socket.h NOD_socket_declarations.hh + NOD_socket_declarations_geometry.hh NOD_static_types.h NOD_texture.h NOD_type_conversions.hh diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a37dcf28757..ea3458af065 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,24 +29,25 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); -void register_node_type_geo_legacy_attribute_transfer(void); -void register_node_type_geo_legacy_curve_set_handles(void); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_attribute_transfer(void); +void register_node_type_geo_legacy_curve_endpoints(void); +void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_curve_set_handles(void); +void register_node_type_geo_legacy_curve_spline_type(void); +void register_node_type_geo_legacy_curve_subdivide(void); void register_node_type_geo_legacy_curve_to_points(void); void register_node_type_geo_legacy_delete_geometry(void); +void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_material_assign(void); void register_node_type_geo_legacy_mesh_to_curve(void); void register_node_type_geo_legacy_points_to_volume(void); -void register_node_type_geo_legacy_select_by_material(void); -void register_node_type_geo_legacy_curve_endpoints(void); -void register_node_type_geo_legacy_curve_spline_type(void); -void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_raycast(void); void register_node_type_geo_legacy_select_by_handle_type(void); -void register_node_type_geo_legacy_curve_subdivide(void); -void register_node_type_geo_legacy_edge_split(void); +void register_node_type_geo_legacy_select_by_material(void); void register_node_type_geo_legacy_subdivision_surface(void); -void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_volume_to_mesh(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); @@ -94,11 +95,13 @@ void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_image_texture(void); void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); +void register_node_type_geo_input_id(void); void register_node_type_geo_input_index(void); -void register_node_type_geo_input_material(void); void register_node_type_geo_input_material_index(void); +void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); void register_node_type_geo_input_radius(void); @@ -145,8 +148,9 @@ void register_node_type_geo_separate_geometry(void); void register_node_type_geo_set_curve_handles(void); void register_node_type_geo_set_curve_radius(void); void register_node_type_geo_set_curve_tilt(void); -void register_node_type_geo_set_material(void); +void register_node_type_geo_set_id(void); void register_node_type_geo_set_material_index(void); +void register_node_type_geo_set_material(void); void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); void register_node_type_geo_set_shade_smooth(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 962e1c3c48f..6e1f21dbae0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -171,10 +171,16 @@ class GeoNodeExecParams { this->check_input_access(identifier, &CPPType::get<T>()); #endif GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out<T>(); + T value = gvalue.relocate_out<T>(); + if constexpr (std::is_same_v<T, GeometrySet>) { + this->check_input_geometry_set(identifier, value); + } + return value; } } + void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const; + /** * Get input as vector for multi input socket with the given identifier. * @@ -211,7 +217,11 @@ class GeoNodeExecParams { #endif GPointer gvalue = provider_->get_input(identifier); BLI_assert(gvalue.is_type<T>()); - return *(const T *)gvalue.get(); + const T &value = *(const T *)gvalue.get(); + if constexpr (std::is_same_v<T, GeometrySet>) { + this->check_input_geometry_set(identifier, value); + } + return value; } } @@ -335,6 +345,8 @@ class GeoNodeExecParams { const GeometryComponent &component, const AttributeDomain default_domain) const; + std::string attribute_producer_name() const; + private: /* Utilities for detecting common errors at when using this class. */ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 830a7d4070c..2a118057a03 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -76,6 +76,31 @@ class GenericValueLog : public ValueLog { } }; +class GFieldValueLog : public ValueLog { + private: + fn::GField field_; + const fn::CPPType &type_; + Vector<std::string> input_tooltips_; + + public: + GFieldValueLog(fn::GField field, bool log_full_field); + + const fn::GField &field() const + { + return field_; + } + + Span<std::string> input_tooltips() const + { + return input_tooltips_; + } + + const fn::CPPType &type() const + { + return type_; + } +}; + struct GeometryAttributeInfo { std::string name; AttributeDomain domain; diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index b6e372470c8..e04dd7f41bd 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -200,6 +200,8 @@ class NodeRef : NonCopyable, NonMovable { PointerRNA *rna() const; StringRefNull idname() const; StringRefNull name() const; + StringRefNull label() const; + StringRefNull label_or_name() const; bNodeType *typeinfo() const; const NodeDeclaration *declaration() const; @@ -285,7 +287,17 @@ class NodeTreeRef : NonCopyable, NonMovable { RightToLeft, }; - Vector<const NodeRef *> toposort(ToposortDirection direction) const; + struct ToposortResult { + Vector<const NodeRef *> sorted_nodes; + /** + * There can't be a correct topologycal sort of the nodes when there is a cycle. The nodes will + * still be sorted to some degree. The caller has to decide whether it can handle non-perfect + * sorts or not. + */ + bool has_cycle = false; + }; + + ToposortResult toposort(ToposortDirection direction) const; bNodeTree *btree() const; StringRefNull name() const; @@ -575,6 +587,20 @@ inline StringRefNull NodeRef::name() const return bnode_->name; } +inline StringRefNull NodeRef::label() const +{ + return bnode_->label; +} + +inline StringRefNull NodeRef::label_or_name() const +{ + const StringRefNull label = this->label(); + if (!label.is_empty()) { + return label; + } + return this->name(); +} + inline bNodeType *NodeRef::typeinfo() const { return bnode_->typeinfo; diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index d4958f433d6..f7aea212f73 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -212,14 +212,6 @@ class Image : public IDSocketDeclaration { Image(); }; -class Geometry : public SocketDeclaration { - public: - using Builder = SocketDeclarationBuilder<Geometry>; - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; -}; - /* -------------------------------------------------------------------- */ /** \name #FloatBuilder Inline Methods * \{ */ @@ -396,9 +388,7 @@ MAKE_EXTERN_SOCKET_DECLARATION(decl::Vector) MAKE_EXTERN_SOCKET_DECLARATION(decl::Bool) MAKE_EXTERN_SOCKET_DECLARATION(decl::Color) MAKE_EXTERN_SOCKET_DECLARATION(decl::String) -MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) -#undef MAKE_EXTERN_SOCKET_DECLARATION } // namespace blender::nodes /** \} */ diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh new file mode 100644 index 00000000000..1531f82d67d --- /dev/null +++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BKE_geometry_set.hh" + +#include "NOD_socket_declarations.hh" + +namespace blender::nodes::decl { + +class GeometryBuilder; + +class Geometry : public SocketDeclaration { + private: + blender::Vector<GeometryComponentType> supported_types_; + bool only_realized_data_ = false; + bool only_instances_ = false; + + friend GeometryBuilder; + + public: + using Builder = GeometryBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + + Span<GeometryComponentType> supported_types() const; + bool only_realized_data() const; + bool only_instances() const; +}; + +class GeometryBuilder : public SocketDeclarationBuilder<Geometry> { + public: + GeometryBuilder &supported_type(GeometryComponentType supported_type); + GeometryBuilder &supported_type(blender::Vector<GeometryComponentType> supported_types); + GeometryBuilder &only_realized_data(bool value = true); + GeometryBuilder &only_instances(bool value = true); +}; + +} // namespace blender::nodes::decl + +namespace blender::nodes { +MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) +} diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index af14538e6cc..20ad4d359f1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -321,21 +321,18 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") -DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "") -DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") -DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") @@ -343,22 +340,21 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") -DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") -DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") -DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") -DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") +DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") +DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") +DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") -DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "") @@ -371,8 +367,8 @@ DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", Inst DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") +DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") @@ -381,7 +377,6 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") @@ -390,29 +385,38 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") +DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "") +DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") -DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") +DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "") DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "") DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "") DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "") +DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") -DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") +DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") +DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 3a4bf94d256..3f8a7606d94 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("Image"); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index ad4b09c69d0..028afad3cf8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -29,10 +29,10 @@ namespace blender::nodes { static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Bright").min(-100.0f).max(100.0f); - b.add_input<decl::Float>("Contrast").min(-100.0f).max(100.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f); + b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 440e37fe741..ef8af5f81a6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 0682c66f1e8..095fbef826a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Mask").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 170fecb251c..4247e81e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_composite_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 88d96e1ca4a..5f99bb57768 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_time_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Fac"); + b.add_output<decl::Float>(N_("Fac")); } } // namespace blender::nodes @@ -90,11 +90,12 @@ namespace blender::nodes { static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Color>("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype( + PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index fd959376afe..c1e64065f7e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Exposure").min(-10.0f).max(10.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index a29a001688a..74152a27485 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -29,10 +29,13 @@ namespace blender::nodes { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype( - PROP_UNSIGNED); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Gamma")) + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc index 07746918a94..21430035465 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc @@ -29,16 +29,20 @@ namespace blender::nodes { static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Saturation") + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Saturation")) .default_value(1.0f) .min(0.0f) .max(2.0f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(2.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index 39014896a7b..83743bbed18 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.cc b/source/blender/nodes/composite/nodes/node_composite_idMask.cc index de011dd6274..5121370567c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void cmp_node_idmask_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("ID value").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Float>("Alpha"); + b.add_input<decl::Float>(N_("ID value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Alpha")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc index 57b7ed36ccd..dabf0452628 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_invert_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Color"); + b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index aaab8dcc874..54064f24e0d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_levels_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_output<decl::Float>("Mean"); - b.add_output<decl::Float>("Std Dev"); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_output<decl::Float>(N_("Mean")); + b.add_output<decl::Float>(N_("Std Dev")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index 8b415bb8b63..6428fadaa5f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void cmp_node_mask_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Mask"); + b.add_output<decl::Float>(N_("Mask")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index ae91212f811..5d63a1b8002 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -30,12 +30,12 @@ namespace blender::nodes { static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("Image"); - b.add_output<decl::Float>("Alpha"); - b.add_output<decl::Float>("Offset X"); - b.add_output<decl::Float>("Offset Y"); - b.add_output<decl::Float>("Scale"); - b.add_output<decl::Float>("Angle"); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Alpha")); + b.add_output<decl::Float>(N_("Offset X")); + b.add_output<decl::Float>(N_("Offset Y")); + b.add_output<decl::Float>(N_("Scale")); + b.add_output<decl::Float>(N_("Angle")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index e557854c611..49068429a8d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index 332e56e26b1..abe69d6a756 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index aa719a99b36..83c54069658 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Float>("H"); - b.add_output<decl::Float>("S"); - b.add_output<decl::Float>("V"); - b.add_output<decl::Float>("A"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("H")); + b.add_output<decl::Float>(N_("S")); + b.add_output<decl::Float>(N_("V")); + b.add_output<decl::Float>(N_("A")); } } // namespace blender::nodes @@ -53,11 +53,11 @@ namespace blender::nodes { static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("H").min(0.0f).max(1.0f); - b.add_input<decl::Float>("S").min(0.0f).max(1.0f); - b.add_input<decl::Float>("V").min(0.0f).max(1.0f); - b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index b29af1359f5..049e798af0a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -28,11 +28,11 @@ namespace blender::nodes { static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Float>("R"); - b.add_output<decl::Float>("G"); - b.add_output<decl::Float>("B"); - b.add_output<decl::Float>("A"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("R")); + b.add_output<decl::Float>(N_("G")); + b.add_output<decl::Float>(N_("B")); + b.add_output<decl::Float>(N_("A")); } } // namespace blender::nodes @@ -53,11 +53,11 @@ namespace blender::nodes { static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("R").min(0.0f).max(1.0f); - b.add_input<decl::Float>("G").min(0.0f).max(1.0f); - b.add_input<decl::Float>("B").min(0.0f).max(1.0f); - b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc index 526d6b4eb5b..eaf6ba5e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Float>("Cb"); - b.add_output<decl::Float>("Cr"); - b.add_output<decl::Float>("A"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Float>(N_("Cb")); + b.add_output<decl::Float>(N_("Cr")); + b.add_output<decl::Float>(N_("A")); } } // namespace blender::nodes @@ -60,11 +60,11 @@ namespace blender::nodes { static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Y").min(0.0f).max(1.0f); - b.add_input<decl::Float>("Cb").default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("Cr").default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc index 4619b0c97f1..bc7710122d1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Float>("U"); - b.add_output<decl::Float>("V"); - b.add_output<decl::Float>("A"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Float>(N_("U")); + b.add_output<decl::Float>(N_("V")); + b.add_output<decl::Float>(N_("A")); } } // namespace blender::nodes @@ -54,11 +54,11 @@ namespace blender::nodes { static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Y").min(0.0f).max(1.0f); - b.add_input<decl::Float>("U").min(0.0f).max(1.0f); - b.add_input<decl::Float>("V").min(0.0f).max(1.0f); - b.add_input<decl::Float>("A").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 07a7ffcb426..f59ba76f0c5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index eff008b4b41..55ae6a4185e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -29,14 +29,14 @@ namespace blender::nodes { static void cmp_node_texture_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Scale") + b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .min(-10.0f) .max(10.0f) .subtype(PROP_XYZ); - b.add_output<decl::Float>("Value"); - b.add_output<decl::Color>("Color"); + b.add_output<decl::Float>(N_("Value")); + b.add_output<decl::Color>(N_("Color")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 85fd240ce2e..33d6f98201c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -27,8 +27,8 @@ namespace blender::nodes { static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index cb5c9468daa..537f7e661db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("X"); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Vector>("Speed").subtype(PROP_VELOCITY); + b.add_output<decl::Float>(N_("X")); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index 9e4f1329fbd..a0ab056e657 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>("Image"); - b.add_output<decl::Float>("Alpha"); + b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Alpha")); } } // namespace blender::nodes @@ -60,8 +60,8 @@ namespace blender::nodes { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output<decl::Color>("Val"); + b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Color>(N_("Val")); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc index 5459801bcc7..51214d23472 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void cmp_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Value").default_value(0.5f); + b.add_output<decl::Float>(N_("Value")).default_value(0.5f); } } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 7234d4d8eb2..b86ae57f664 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -32,9 +32,9 @@ namespace blender::nodes { static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); } } // namespace blender::nodes diff --git a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc index 7f6f554ba93..d98d49c7273 100644 --- a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Float>(N_("Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Float>(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc index ae41cdfca5a..4088fa24ca7 100644 --- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -28,10 +28,14 @@ namespace blender::nodes { static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value(); - b.add_input<decl::Float>("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Vector>("Vector").default_value({0.0, 0.0, 1.0}); - b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input<decl::Float>(N_("Factor")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Vector")).default_value({0.0, 0.0, 1.0}); + b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); } static void fn_node_align_euler_to_vector_layout(uiLayout *layout, diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 09caf12e425..b44e8d54ff1 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Bool>("Boolean", "Boolean"); - b.add_input<decl::Bool>("Boolean", "Boolean_001"); - b.add_output<decl::Bool>("Boolean"); + b.add_input<decl::Bool>(N_("Boolean"), "Boolean"); + b.add_input<decl::Bool>(N_("Boolean"), "Boolean_001"); + b.add_output<decl::Bool>(N_("Boolean")); }; static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index bdc4a3c1e02..2e1f2aaeeef 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -31,10 +31,10 @@ namespace blender::nodes { static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Bool>("Result"); + b.add_input<decl::Float>(N_("A")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("B")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Epsilon")).default_value(0.001f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Bool>(N_("Result")); }; static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 5dccd26158b..e6ec925f945 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -30,8 +30,8 @@ namespace blender::nodes { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Float"); - b.add_output<decl::Int>("Integer"); + b.add_input<decl::Float>(N_("Float")); + b.add_output<decl::Int>(N_("Integer")); }; static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc index 58f8969f1b6..1358bf8a223 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_bool_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Boolean"); + b.add_output<decl::Bool>(N_("Boolean")); }; static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index b6079835eae..43bb654b776 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void fn_node_input_color_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("Color"); + b.add_output<decl::Color>(N_("Color")); }; static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc index db52d569ac5..ddbb86e2661 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_int_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>("Integer"); + b.add_output<decl::Int>(N_("Integer")); }; static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc index 11c64d3f694..c61af419e50 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc @@ -20,8 +20,8 @@ namespace blender::nodes { static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::String>("Line Break"); - b.add_output<decl::String>("Tab"); + b.add_output<decl::String>(N_("Line Break")); + b.add_output<decl::String>(N_("Tab")); }; class MF_SpecialCharacters : public fn::MultiFunction { diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 0982096eaea..dd2d1292601 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_output<decl::String>("String"); + b.add_output<decl::String>(N_("String")); }; static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index f64fd182282..1e5fd186b5a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Vector"); + b.add_output<decl::Vector>(N_("Vector")); }; static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc index 53ca77aab0c..d48b9f3461a 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_value.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -26,29 +26,29 @@ namespace blender::nodes { static void fn_node_random_value_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Min").supports_field(); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field(); - b.add_input<decl::Float>("Min", "Min_001").supports_field(); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field(); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field(); - b.add_input<decl::Int>("Max", "Max_002") + b.add_input<decl::Vector>(N_("Min")).supports_field(); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}).supports_field(); + b.add_input<decl::Float>(N_("Min"), "Min_001").supports_field(); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f).supports_field(); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000).supports_field(); + b.add_input<decl::Int>(N_("Max"), "Max_002") .default_value(100) .min(-100000) .max(100000) .supports_field(); - b.add_input<decl::Float>("Probability") + b.add_input<decl::Float>(N_("Probability")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input<decl::Int>("ID").implicit_field(); - b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field(); + b.add_input<decl::Int>(N_("ID")).implicit_field(); + b.add_input<decl::Int>(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field(); - b.add_output<decl::Vector>("Value").dependent_field(); - b.add_output<decl::Float>("Value", "Value_001").dependent_field(); - b.add_output<decl::Int>("Value", "Value_002").dependent_field(); - b.add_output<decl::Bool>("Value", "Value_003").dependent_field(); + b.add_output<decl::Vector>(N_("Value")).dependent_field(); + b.add_output<decl::Float>(N_("Value"), "Value_001").dependent_field(); + b.add_output<decl::Int>(N_("Value"), "Value_002").dependent_field(); + b.add_output<decl::Bool>(N_("Value"), "Value_003").dependent_field(); } static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc index 1ec4979176e..881a3c68e7d 100644 --- a/source/blender/nodes/function/nodes/node_fn_replace_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -22,10 +22,11 @@ namespace blender::nodes { static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("String"); - b.add_input<decl::String>("Find").description("The string to find in the input string"); - b.add_input<decl::String>("Replace").description("The string to replace each match with"); - b.add_output<decl::String>("String"); + b.add_input<decl::String>(N_("String")); + b.add_input<decl::String>(N_("Find")).description(N_("The string to find in the input string")); + b.add_input<decl::String>(N_("Replace")) + .description(N_("The string to replace each match with")); + b.add_output<decl::String>(N_("String")); }; static std::string replace_all(std::string str, const std::string &from, const std::string &to) diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index a01cc6b58dd..fc4c3d8221f 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -29,11 +29,11 @@ namespace blender::nodes { static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value(); - b.add_input<decl::Vector>("Rotate By").subtype(PROP_EULER); - b.add_input<decl::Vector>("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE); - b.add_output<decl::Vector>("Rotation"); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input<decl::Vector>(N_("Rotate By")).subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); + b.add_output<decl::Vector>(N_("Rotation")); }; static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/function/nodes/node_fn_slice_string.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc index 08e17da0d92..5cb753e8f34 100644 --- a/source/blender/nodes/function/nodes/node_fn_slice_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc @@ -22,10 +22,10 @@ namespace blender::nodes { static void fn_node_slice_string_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("String"); - b.add_input<decl::Int>("Position"); - b.add_input<decl::Int>("Length").min(0).default_value(10); - b.add_output<decl::String>("String"); + b.add_input<decl::String>(N_("String")); + b.add_input<decl::Int>(N_("Position")); + b.add_input<decl::Int>(N_("Length")).min(0).default_value(10); + b.add_output<decl::String>(N_("String")); }; static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index d882280b566..63429d35993 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -24,8 +24,8 @@ namespace blender::nodes { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("String"); - b.add_output<decl::Int>("Length"); + b.add_input<decl::String>(N_("String")); + b.add_output<decl::Int>(N_("Length")); }; static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index 112726f98dc..96a56760664 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -21,9 +21,9 @@ namespace blender::nodes { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Value"); - b.add_input<decl::Int>("Decimals").min(0); - b.add_output<decl::String>("String"); + b.add_input<decl::Float>(N_("Value")); + b.add_input<decl::Int>(N_("Decimals")).min(0); + b.add_output<decl::String>(N_("String")); }; static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index f382ff6c132..167765fa131 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -32,6 +32,7 @@ #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "node_util.h" diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc index d0bb906e8af..b92d4704d63 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Float>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Float>(N_("Factor"), "Factor_001") .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001") + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001") .default_value({0.0, 0.0, 1.0}) .subtype(PROP_ANGLE); - b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc index 2e931a2da98..91ff114a480 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc @@ -24,18 +24,18 @@ namespace blender::nodes { static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_input<decl::Vector>("Min"); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Min", "Min_001"); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Color>("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::Color>("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_input<decl::Vector>(N_("Min")); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Min"), "Min_001"); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Color>(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::Color>(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc index aa054af3acd..ab4b6aad545 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_color_ramp_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc index 569d5a824ca..d4c23380b4e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc @@ -23,15 +23,15 @@ namespace blender::nodes { static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("X"); - b.add_input<decl::Float>("X", "X_001"); - b.add_input<decl::String>("Y"); - b.add_input<decl::Float>("Y", "Y_001"); - b.add_input<decl::String>("Z"); - b.add_input<decl::Float>("Z", "Z_001"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("X")); + b.add_input<decl::Float>(N_("X"), "X_001"); + b.add_input<decl::String>(N_("Y")); + b.add_input<decl::Float>(N_("Y"), "Y_001"); + b.add_input<decl::String>(N_("Z")); + b.add_input<decl::Float>(N_("Z"), "Z_001"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc index 0b9708dae14..e4e43a7b724 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc @@ -25,18 +25,18 @@ namespace blender::nodes { static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::Vector>("A", "A_002"); - b.add_input<decl::Color>("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::Vector>("B", "B_002"); - b.add_input<decl::Color>("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::Float>("Threshold").default_value(0.01f).min(0.0f); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::Vector>(N_("A"), "A_002"); + b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::Vector>(N_("B"), "B_002"); + b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.01f).min(0.0f); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_compare_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc index a2382aa9d25..dc05fa2c125 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_convert_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc index b9621b4ae92..669ac21436f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_curve_map_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc index 1458b6df9ba..5cb49dd83d0 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc @@ -23,14 +23,14 @@ namespace blender::nodes { static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute").is_attribute_name(); - b.add_input<decl::Vector>("Value", "Value"); - b.add_input<decl::Float>("Value", "Value_001"); - b.add_input<decl::Color>("Value", "Value_002"); - b.add_input<decl::Bool>("Value", "Value_003"); - b.add_input<decl::Int>("Value", "Value_004"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")).is_attribute_name(); + b.add_input<decl::Vector>(N_("Value"), "Value"); + b.add_input<decl::Float>(N_("Value"), "Value_001"); + b.add_input<decl::Color>(N_("Value"), "Value_002"); + b.add_input<decl::Bool>(N_("Value"), "Value_003"); + b.add_input<decl::Int>(N_("Value"), "Value_004"); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc index 0ea3bbe1e45..978c75187fe 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc @@ -26,21 +26,21 @@ namespace blender::nodes { static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_input<decl::Float>("From Min"); - b.add_input<decl::Float>("From Max").default_value(1.0f); - b.add_input<decl::Float>("To Min"); - b.add_input<decl::Float>("To Max").default_value(1.0f); - b.add_input<decl::Float>("Steps").default_value(4.0f); - b.add_input<decl::Vector>("From Min", "From Min_001"); - b.add_input<decl::Vector>("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>("To Min", "To Min_001"); - b.add_input<decl::Vector>("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f}); - b.add_input<decl::Bool>("Clamp"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_input<decl::Float>(N_("From Min")); + b.add_input<decl::Float>(N_("From Max")).default_value(1.0f); + b.add_input<decl::Float>(N_("To Min")); + b.add_input<decl::Float>(N_("To Max")).default_value(1.0f); + b.add_input<decl::Float>(N_("Steps")).default_value(4.0f); + b.add_input<decl::Vector>(N_("From Min"), "From Min_001"); + b.add_input<decl::Vector>(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("To Min"), "To Min_001"); + b.add_input<decl::Vector>(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f}); + b.add_input<decl::Bool>(N_("Clamp")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc index efa09215b45..55d35f87cda 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc @@ -29,15 +29,15 @@ namespace blender::nodes { static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::String>("C"); - b.add_input<decl::Float>("C", "C_001"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::String>(N_("C")); + b.add_input<decl::Float>(N_("C"), "C_001"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static bool operation_use_input_c(const NodeMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc index 74e05cb997d..b4205bc91b7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc @@ -29,23 +29,23 @@ namespace blender::nodes { static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Float>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Float>(N_("Factor"), "Factor_001") .default_value(0.5f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::Vector>("A", "A_002"); - b.add_input<decl::Color>("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::Vector>("B", "B_002"); - b.add_input<decl::Color>("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::Vector>(N_("A"), "A_002"); + b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::Vector>(N_("B"), "B_002"); + b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc index 6120118f611..9e3a7984c53 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc @@ -30,11 +30,11 @@ namespace blender::nodes { static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Target"); - b.add_input<decl::String>("Distance"); - b.add_input<decl::String>("Position"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Target")); + b.add_input<decl::String>(N_("Distance")); + b.add_input<decl::String>(N_("Position")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_proximity_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc index 2e6ba456725..2901472d661 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc @@ -27,16 +27,16 @@ namespace blender::nodes { static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::Vector>("Min"); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Min", "Min_001"); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::Vector>(N_("Min")); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Min"), "Min_001"); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_legacy_attribute_random_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc index 52f97475941..19d6ced6eb6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc @@ -32,11 +32,11 @@ namespace blender::nodes { static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Texture>("Texture").hide_label(); - b.add_input<decl::String>("Mapping"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Texture>(N_("Texture")).hide_label(); + b.add_input<decl::String>(N_("Mapping")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static AttributeDomain get_result_domain(const GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc index de0090406c6..809e75e73a3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc @@ -23,13 +23,13 @@ namespace blender::nodes { static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001"); - b.add_input<decl::String>("Result X"); - b.add_input<decl::String>("Result Y"); - b.add_input<decl::String>("Result Z"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001"); + b.add_input<decl::String>(N_("Result X")); + b.add_input<decl::String>(N_("Result Y")); + b.add_input<decl::String>(N_("Result Z")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index d7a66dac3ad..3a9cd52661a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -33,11 +33,11 @@ namespace blender::nodes { static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Source Geometry"); - b.add_input<decl::String>("Source"); - b.add_input<decl::String>("Destination"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Source Geometry")); + b.add_input<decl::String>(N_("Source")); + b.add_input<decl::String>(N_("Destination")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_transfer_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc index 59903050f88..4c351846243 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc @@ -30,17 +30,17 @@ namespace blender::nodes { static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Vector>("A", "A_001"); - b.add_input<decl::String>("B"); - b.add_input<decl::Vector>("B", "B_001"); - b.add_input<decl::Float>("B", "B_002"); - b.add_input<decl::String>("C"); - b.add_input<decl::Vector>("C", "C_001"); - b.add_input<decl::Float>("C", "C_002"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Vector>(N_("A"), "A_001"); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Vector>(N_("B"), "B_001"); + b.add_input<decl::Float>(N_("B"), "B_002"); + b.add_input<decl::String>(N_("C")); + b.add_input<decl::Vector>(N_("C"), "C_001"); + b.add_input<decl::Float>(N_("C"), "C_002"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static bool operation_use_input_b(const NodeVectorMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc index 0c515fa63fb..9ab8ec25fb6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc @@ -25,21 +25,21 @@ namespace blender::nodes { static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); - b.add_input<decl::String>("Center"); - b.add_input<decl::Vector>("Center", "Center_001").subtype(PROP_XYZ); - b.add_input<decl::String>("Axis"); - b.add_input<decl::Vector>("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); - b.add_input<decl::String>("Angle"); - b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>("Rotation"); - b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_input<decl::Bool>("Invert"); - b.add_input<decl::String>("Result"); - - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::String>(N_("Center")); + b.add_input<decl::Vector>(N_("Center"), "Center_001").subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Axis")); + b.add_input<decl::Vector>(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Angle")); + b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>(N_("Rotation")); + b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_input<decl::Bool>(N_("Invert")); + b.add_input<decl::String>(N_("Result")); + + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index 85d1392aa35..8b81008ff34 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Start Points"); - b.add_output<decl::Geometry>("End Points"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Start Points")); + b.add_output<decl::Geometry>(N_("End Points")); } /** diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc index d1c81333c30..ba76fafe3e6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc index 0d3de7ac5f5..40d827ae141 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc index 339029336d9..4bac9cb976e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc @@ -25,9 +25,9 @@ namespace blender::nodes { static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_set_handles_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc index 44522e990d9..df53c96e6ca 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc index 61165902028..f9b0a9d128e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc @@ -33,10 +33,10 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Cuts"); - b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Cuts")); + b.add_input<decl::Int>(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc index 2936c150376..c171d485a6a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -30,10 +30,10 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000); - b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000); + b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc index 2d9b4da4c83..1d76a0532a1 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc @@ -47,10 +47,10 @@ namespace blender::nodes { static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Selection"); - b.add_input<decl::Bool>("Invert"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Selection")); + b.add_input<decl::Bool>(N_("Invert")); + b.add_output<decl::Geometry>(N_("Geometry")); } template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc index d7e908edf61..8f2bf05d2b4 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc @@ -26,15 +26,15 @@ namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Edge Angle").default_value(true); - b.add_input<decl::Float>("Angle") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Edge Angle")).default_value(true); + b.add_input<decl::Float>(N_("Angle")) .default_value(DEG2RADF(30.0f)) .min(0.0f) .max(DEG2RADF(180.0f)) .subtype(PROP_ANGLE); - b.add_input<decl::Bool>("Sharp Edges"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Bool>(N_("Sharp Edges")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_edge_split_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc index 7d3481c1067..333a17aa4e9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Material").hide_label(true); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Material>(N_("Material")).hide_label(true); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc index 7a27e856cef..9167096fd3d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc @@ -22,9 +22,9 @@ namespace blender::nodes { static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Mesh")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc index f30feb48734..210757f986d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc @@ -42,16 +42,16 @@ namespace blender::nodes { static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Density Max") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Density Max")) .default_value(1.0f) .min(0.0f) .max(100000.0f) .subtype(PROP_NONE); - b.add_input<decl::String>("Density Attribute"); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("Density Attribute")); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_point_distribute_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc index fb45c22ced4..ffb2a0dd7ac 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Object>("Object").hide_label(); - b.add_input<decl::Collection>("Collection").hide_label(); - b.add_input<decl::Geometry>("Instance Geometry"); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Object>(N_("Object")).hide_label(); + b.add_input<decl::Collection>(N_("Collection")).hide_label(); + b.add_input<decl::Geometry>(N_("Instance Geometry")); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -184,7 +184,7 @@ static void add_instances_from_component(InstancesComponent &instances, instances.resize(start_len + domain_size); MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size); MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size); - MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size); + MutableSpan<int> instance_ids = instances.instance_ids_ensure().slice(start_len, domain_size); /* Skip all of the randomness handling if there is only a single possible instance * (anything except for collection mode with "Whole Collection" turned off). */ diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc index 60c82360007..54d36dab98d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc @@ -25,14 +25,16 @@ namespace blender::nodes { static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Axis"); - b.add_input<decl::Vector>("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input<decl::String>("Angle"); - b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>("Rotation"); - b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Axis")); + b.add_input<decl::Vector>(N_("Axis"), "Axis_001") + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Angle")); + b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>(N_("Rotation")); + b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc index 99adce149e9..934442ee8a3 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc @@ -25,13 +25,13 @@ namespace blender::nodes { static void geo_node_point_scale_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Vector>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Vector>(N_("Factor"), "Factor_001") .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ); - b.add_input<decl::Float>("Factor", "Factor_002").default_value(1.0f).min(0.0f); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Float>(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc index 48b6676c1dd..accdaf78439 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Mask"); - b.add_output<decl::Geometry>("Geometry 1"); - b.add_output<decl::Geometry>("Geometry 2"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Mask")); + b.add_output<decl::Geometry>(N_("Geometry 1")); + b.add_output<decl::Geometry>(N_("Geometry 2")); } template<typename T> diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc index f2fce45c57b..34f7641995f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_point_translate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Translation"); - b.add_input<decl::Vector>("Translation", "Translation_001").subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Translation")); + b.add_input<decl::Vector>(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc index 68d3f232ce1..cf7f466c2a6 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc @@ -32,13 +32,13 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f); - b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input<decl::String>("Radius"); - b.add_input<decl::Float>("Radius", "Radius_001").default_value(0.5f).min(0.0f); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::String>(N_("Radius")); + b.add_input<decl::Float>(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_points_to_volume_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc index 6641e622362..e6a81fc9627 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc @@ -28,23 +28,23 @@ namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Target Geometry"); - b.add_input<decl::String>("Ray Direction"); - b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Target Geometry")); + b.add_input<decl::String>(N_("Ray Direction")); + b.add_input<decl::Vector>(N_("Ray Direction"), "Ray Direction_001") .default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::String>("Ray Length"); - b.add_input<decl::Float>("Ray Length", "Ray Length_001") + b.add_input<decl::String>(N_("Ray Length")); + b.add_input<decl::Float>(N_("Ray Length"), "Ray Length_001") .default_value(100.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input<decl::String>("Target Attribute"); - b.add_input<decl::String>("Is Hit"); - b.add_input<decl::String>("Hit Position"); - b.add_input<decl::String>("Hit Normal"); - b.add_input<decl::String>("Hit Distance"); - b.add_input<decl::String>("Hit Attribute"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("Target Attribute")); + b.add_input<decl::String>(N_("Is Hit")); + b.add_input<decl::String>(N_("Hit Position")); + b.add_input<decl::String>(N_("Hit Normal")); + b.add_input<decl::String>(N_("Hit Distance")); + b.add_input<decl::String>(N_("Hit Attribute")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc index eabdd2bcd5a..a8d6f33a5fd 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc @@ -30,10 +30,10 @@ namespace blender::nodes { static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Material").hide_label(); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Material>(N_("Material")).hide_label(); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc index 101c915eb77..295cd05fd01 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); - b.add_input<decl::Bool>("Use Creases"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_input<decl::Bool>(N_("Use Creases")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_subdivision_surface_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc new file mode 100644 index 00000000000..39af5bf1fd2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc @@ -0,0 +1,173 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DEG_depsgraph_query.h" +#ifdef WITH_OPENVDB +# include <openvdb/tools/GridTransformer.h> +# include <openvdb/tools/VolumeToMesh.h> +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Density")); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( + sizeof(NodeGeometryVolumeToMesh), __func__); + data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; + + bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); + bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; + STRNCPY(grid_socket_value->value, "density"); + + node->storage = data; +} + +static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(voxel_size_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static void create_mesh_from_volume(GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + GeoNodeExecParams ¶ms) +{ + if (!geometry_set_in.has<VolumeComponent>()) { + return; + } + + const NodeGeometryVolumeToMesh &storage = + *(const NodeGeometryVolumeToMesh *)params.node().storage; + + bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return; + } + } + else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + const Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + const std::string grid_name = params.get_input<std::string>("Density"); + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input<float>("Threshold"); + float adaptivity = params.get_input<float>("Adaptivity"); + + const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); + if (mesh == nullptr) { + return; + } + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_volume_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); + node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); + ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; + ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index b7352160f89..5cc8f1476f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -25,19 +25,19 @@ namespace blender::nodes { static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Vector>("Value").supports_field(); - b.add_input<decl::Float>("Value", "Value_001").supports_field(); - b.add_input<decl::Color>("Value", "Value_002").supports_field(); - b.add_input<decl::Bool>("Value", "Value_003").supports_field(); - b.add_input<decl::Int>("Value", "Value_004").supports_field(); - - b.add_output<decl::Geometry>("Geometry"); - b.add_output<decl::Vector>("Attribute").field_source(); - b.add_output<decl::Float>("Attribute", "Attribute_001").field_source(); - b.add_output<decl::Color>("Attribute", "Attribute_002").field_source(); - b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source(); - b.add_output<decl::Int>("Attribute", "Attribute_004").field_source(); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Vector>(N_("Value")).supports_field(); + b.add_input<decl::Float>(N_("Value"), "Value_001").supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_003").supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_004").supports_field(); + + b.add_output<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Vector>(N_("Attribute")).field_source(); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").field_source(); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").field_source(); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").field_source(); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").field_source(); } static void geo_node_attribute_capture_layout(uiLayout *layout, @@ -144,7 +144,7 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) break; } - WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + WeakAnonymousAttributeID anonymous_id{"Attribute"}; const CPPType &type = field.cpp_type(); static const Array<GeometryComponentType> types = { @@ -158,8 +158,8 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) } }); - GField output_field{ - std::make_shared<bke::AnonymousAttributeFieldInput>(std::move(anonymous_id), type)}; + GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>( + std::move(anonymous_id), type, params.attribute_producer_name())}; switch (data_type) { case CD_PROP_FLOAT: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index f93ef6f1db3..f80b8ccc971 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -20,9 +20,9 @@ namespace blender::nodes { static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute").multi_input(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")).multi_input(); + b.add_output<decl::Geometry>(N_("Geometry")); } static void remove_attribute(GeometryComponent &component, @@ -59,6 +59,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); } + if (geometry_set.has<InstancesComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1b7d2fe28a1..155bd8c8c28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -28,27 +28,27 @@ namespace blender::nodes { static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Attribute").hide_value().supports_field(); - b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field(); - - b.add_output<decl::Float>("Mean"); - b.add_output<decl::Float>("Median"); - b.add_output<decl::Float>("Sum"); - b.add_output<decl::Float>("Min"); - b.add_output<decl::Float>("Max"); - b.add_output<decl::Float>("Range"); - b.add_output<decl::Float>("Standard Deviation"); - b.add_output<decl::Float>("Variance"); - - b.add_output<decl::Vector>("Mean", "Mean_001"); - b.add_output<decl::Vector>("Median", "Median_001"); - b.add_output<decl::Vector>("Sum", "Sum_001"); - b.add_output<decl::Vector>("Min", "Min_001"); - b.add_output<decl::Vector>("Max", "Max_001"); - b.add_output<decl::Vector>("Range", "Range_001"); - b.add_output<decl::Vector>("Standard Deviation", "Standard Deviation_001"); - b.add_output<decl::Vector>("Variance", "Variance_001"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + + b.add_output<decl::Float>(N_("Mean")); + b.add_output<decl::Float>(N_("Median")); + b.add_output<decl::Float>(N_("Sum")); + b.add_output<decl::Float>(N_("Min")); + b.add_output<decl::Float>(N_("Max")); + b.add_output<decl::Float>(N_("Range")); + b.add_output<decl::Float>(N_("Standard Deviation")); + b.add_output<decl::Float>(N_("Variance")); + + b.add_output<decl::Vector>(N_("Mean"), "Mean_001"); + b.add_output<decl::Vector>(N_("Median"), "Median_001"); + b.add_output<decl::Vector>(N_("Sum"), "Sum_001"); + b.add_output<decl::Vector>(N_("Min"), "Min_001"); + b.add_output<decl::Vector>(N_("Max"), "Max_001"); + b.add_output<decl::Vector>(N_("Range"), "Range_001"); + b.add_output<decl::Vector>(N_("Standard Deviation"), "Standard Deviation_001"); + b.add_output<decl::Vector>(N_("Variance"), "Variance_001"); } static void geo_node_attribute_statistic_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 27e18f16bad..516f07b7ad3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -27,11 +27,13 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry 1"); - b.add_input<decl::Geometry>("Geometry 2").multi_input(); - b.add_input<decl::Bool>("Self Intersection"); - b.add_input<decl::Bool>("Hole Tolerant"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh 1")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Geometry>(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Self Intersection")); + b.add_input<decl::Bool>(N_("Hole Tolerant")); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -51,12 +53,12 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node) case GEO_NODE_BOOLEAN_UNION: nodeSetSocketAvailability(geometry_1_socket, false); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry")); + node_sock_label(geometry_2_socket, N_("Mesh")); break; case GEO_NODE_BOOLEAN_DIFFERENCE: nodeSetSocketAvailability(geometry_1_socket, true); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry 2")); + node_sock_label(geometry_2_socket, N_("Mesh 2")); break; } } @@ -82,12 +84,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { - set_a = params.extract_input<GeometrySet>("Geometry 1"); - if (set_a.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("Instances are not supported for the first geometry input, and will not be used")); - } + set_a = params.extract_input<GeometrySet>("Mesh 1"); /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ @@ -101,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector<bke::GeometryInstanceGroup> set_groups; - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2"); + Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -119,7 +116,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) Mesh *result = blender::meshintersect::direct_mesh_boolean( meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation); - params.set_output("Geometry", GeometrySet::create_with_mesh(result)); + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index e34b65e6c94..e7c9715934a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Bounding Box"); - b.add_output<decl::Vector>("Min"); - b.add_output<decl::Vector>("Max"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Bounding Box")); + b.add_output<decl::Vector>(N_("Min")); + b.add_output<decl::Vector>(N_("Max")); } static void geo_node_bounding_box_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index eca4e3d2d14..f068e621596 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -31,15 +31,15 @@ namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Collection>("Collection").hide_label(); - b.add_input<decl::Bool>("Separate Children") + b.add_input<decl::Collection>(N_("Collection")).hide_label(); + b.add_input<decl::Bool>(N_("Separate Children")) .description( - "Output each child of the collection as a separate instance, sorted alphabetically"); - b.add_input<decl::Bool>("Reset Children") + N_("Output each child of the collection as a separate instance, sorted alphabetically")); + b.add_input<decl::Bool>(N_("Reset Children")) .description( - "Reset the transforms of every child instance in the output. Only used when Separate " - "Children is enabled"); - b.add_output<decl::Geometry>("Geometry"); + N_("Reset the transforms of every child instance in the output. Only used when Separate " + "Children is enabled")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -71,6 +71,14 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set_out); return; } + const Object *self_object = params.self_object(); + const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection, + (Object *)self_object); + if (is_recursive) { + params.error_message_add(NodeWarningType::Error, "Collection contains current object"); + params.set_output("Geometry", geometry_set_out); + return; + } const bNode &bnode = params.node(); NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; @@ -79,8 +87,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - const Object *self_object = params.self_object(); - const bool separate_children = params.get_input<bool>("Separate Children"); if (separate_children) { const bool reset_children = params.get_input<bool>("Reset Children"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 4377d32210d..3cf682e161c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -32,8 +32,8 @@ namespace blender::nodes { static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Convex Hull"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Convex Hull")); } using bke::GeometryInstanceGroup; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index ee6cf055ecb..42d88cdb1e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -25,19 +25,20 @@ namespace blender::nodes { static void geo_node_curve_endpoint_selection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Start Size") + b.add_input<decl::Int>(N_("Start Size")) .min(0) .default_value(1) .supports_field() - .description("The amount of points to select from the start of each spline"); - b.add_input<decl::Int>("End Size") + .description(N_("The amount of points to select from the start of each spline")); + b.add_input<decl::Int>(N_("End Size")) .min(0) .default_value(1) .supports_field() - .description("The amount of points to select from the end of each spline"); - b.add_output<decl::Bool>("Selection") + .description(N_("The amount of points to select from the end of each spline")); + b.add_output<decl::Bool>(N_("Selection")) .field_source() - .description("The selection from the start and end of the splines based on the input sizes"); + .description( + N_("The selection from the start and end of the splines based on the input sizes")); } static void select_by_spline(const int start, const int end, MutableSpan<bool> r_selection) @@ -56,8 +57,11 @@ class EndpointFieldInput final : public fn::FieldInput { public: EndpointFieldInput(Field<int> start_size, Field<int> end_size) - : FieldInput(CPPType::get<bool>(), "Selection"), start_size_(start_size), end_size_(end_size) + : FieldInput(CPPType::get<bool>(), "Endpoint Selection node"), + start_size_(start_size), + end_size_(end_size) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 953922531c1..219effadec4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -35,8 +35,8 @@ namespace blender::nodes { static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 67ce5b00d6b..27d7d22b106 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -29,16 +29,16 @@ namespace blender::nodes { static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Int>("Count").default_value(1).min(1).max(1000).supports_field(); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Count")).default_value(1).min(1).max(1000).supports_field(); + b.add_input<decl::Float>(N_("Radius")) .min(0.0f) .max(FLT_MAX) .subtype(PropertySubType::PROP_DISTANCE) .default_value(0.25f) .supports_field(); - b.add_input<decl::Bool>("Limit Radius"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Bool>(N_("Limit Radius")); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index b565b1e4602..165f5da5f71 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void geo_node_curve_handle_type_selection_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Selection").field_source(); + b.add_output<decl::Bool>(N_("Selection")).field_source(); } static void geo_node_curve_handle_type_selection_layout(uiLayout *layout, @@ -91,8 +91,9 @@ class HandleTypeFieldInput final : public fn::FieldInput { public: HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) - : FieldInput(CPPType::get<bool>(), "Selection"), type_(type), mode_(mode) + : FieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index ac7df35bb72..0d0dc0ec89c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -21,8 +21,8 @@ namespace blender::nodes { static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_output<decl::Float>("Length"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output<decl::Float>(N_("Length")); } static void geo_node_curve_length_exec(GeoNodeExecParams params) 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 90853387ec7..4c89aba2e6d 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,7 @@ namespace blender::nodes { static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Factor").field_source(); + b.add_output<decl::Float>(N_("Factor")).field_source(); } /** @@ -150,8 +150,9 @@ static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, class CurveParameterFieldInput final : public fn::FieldInput { public: - CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter") + CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, 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 313473e3442..a755d47cc6a 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 @@ -25,14 +25,20 @@ namespace blender::nodes { static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Start Handle") + b.add_input<decl::Int>(N_("Resolution")) + .default_value(16) + .min(1) + .max(256) + .subtype(PROP_UNSIGNED); + b.add_input<decl::Vector>(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End Handle").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Curve"); + 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); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, 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 8d5f4855512..bf4f22d6578 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,15 +25,19 @@ namespace blender::nodes { static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(32).min(3).max(512); - b.add_input<decl::Vector>("Point 1") + b.add_input<decl::Int>(N_("Resolution")).default_value(32).min(3).max(512); + b.add_input<decl::Vector>(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); - b.add_output<decl::Vector>("Center"); + b.add_input<decl::Vector>(N_("Point 2")) + .default_value({0.0f, 1.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + 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); + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Vector>(N_("Center")); } static void geo_node_curve_primitive_circle_layout(uiLayout *layout, 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 a3d2ada612f..5b215797052 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,11 +25,11 @@ namespace blender::nodes { static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Start").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Direction").default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Length").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); + 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_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_primitive_line_layout(uiLayout *layout, 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 a54fd971ac4..6041ddee02d 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 @@ -21,11 +21,19 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Resolution")) + .default_value(16) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED); + b.add_input<decl::Vector>(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION); + 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); + b.add_output<decl::Geometry>(N_("Curve")); } static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, 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 07ddaa8f61e..7260da05a8d 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,18 +23,32 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Offset").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); + 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_("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); + 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); + b.add_input<decl::Vector>(N_("Point 1")) + .default_value({-1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input<decl::Vector>(N_("Point 2")) + .default_value({1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input<decl::Vector>(N_("Point 3")) + .default_value({1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_input<decl::Vector>(N_("Point 4")) + .default_value({-1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, 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 7292fafc8b0..1dc9cd7f107 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 @@ -22,13 +22,17 @@ namespace blender::nodes { static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED); - b.add_input<decl::Float>("Rotations").default_value(2.0f).min(0.0f); - b.add_input<decl::Float>("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("End Radius").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Height").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Bool>("Reverse"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Resolution")) + .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")); + b.add_output<decl::Geometry>(N_("Curve")); } static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, 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 6261146562d..b5bafce17c6 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,11 +22,17 @@ namespace blender::nodes { static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Float>("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Twist").subtype(PROP_ANGLE); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Points")).default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); + b.add_input<decl::Float>(N_("Inner Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + 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); + b.add_output<decl::Geometry>(N_("Curve")); } static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, 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 2617b2f6646..945dac5650b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -34,11 +34,14 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000).supports_field(); - b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).supports_field().subtype( - PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(100000).supports_field(); + b.add_input<decl::Float>(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -79,7 +82,7 @@ static SplinePtr resample_spline(const Spline &src, const int count) Spline::copy_base_settings(src, *dst); if (src.evaluated_edges_size() < 1 || count == 1) { - dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); + dst->add_point(src.positions().first(), src.radii().first(), src.tilts().first()); dst->attributes.reallocate(1); src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { @@ -234,7 +237,7 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set, static void geo_node_resample_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; @@ -244,7 +247,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { Field<int> count = params.extract_input<Field<int>>("Count"); if (count < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Curve", GeometrySet()); return; } mode_param.count.emplace(count); @@ -257,7 +260,7 @@ static void geo_node_resample_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index d4ccb768713..745012c1851 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 6d371c27d43..31b38c0dce7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -27,13 +27,15 @@ namespace blender::nodes { static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); - - b.add_output<decl::Vector>("Position").dependent_field(); - b.add_output<decl::Vector>("Tangent").dependent_field(); - b.add_output<decl::Vector>("Normal").dependent_field(); + b.add_input<decl::Geometry>(N_("Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Float>(N_("Factor")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input<decl::Float>(N_("Length")).min(0.0f).subtype(PROP_DISTANCE).supports_field(); + + b.add_output<decl::Vector>(N_("Position")).dependent_field(); + b.add_output<decl::Vector>(N_("Tangent")).dependent_field(); + b.add_output<decl::Vector>(N_("Normal")).dependent_field(); } static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -237,12 +239,6 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params) params.set_output("Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); }; - if (geometry_set.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized curve data, instances are ignored")); - } - const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); if (component == nullptr) { return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 9e7ac60c29d..8b0a6ca840c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -25,9 +25,9 @@ namespace blender::nodes { static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_set_handles_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index ec72154db13..ae4453929ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_spline_type_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 5a79fcffd4d..b52de822c22 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -33,9 +33,9 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Cuts").default_value(1).min(0).max(1000).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static Array<int> get_subdivided_offsets(const Spline &spline, @@ -328,7 +328,7 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, static void geo_node_subdivide_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -352,7 +352,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); geometry_set.replace_curve(output_curve.release()); }); - params.set_output("Geometry", geometry_set); + params.set_output("Curve", geometry_set); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 00451946af9..1977b465de4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,21 +27,29 @@ namespace blender::nodes { static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Geometry>("Profile Curve"); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Geometry>(N_("Profile Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Fill Caps")) + .description( + N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, + const GeometrySet &profile_set, + const bool fill_caps) { + const CurveEval *curve = geometry_set.get_curve_for_read(); const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read()); + Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { - Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve); + Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } } @@ -50,33 +58,17 @@ static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); - - if (profile_set.has_instances()) { - params.error_message_add(NodeWarningType::Error, - TIP_("Instances are not supported in the profile input")); - params.set_output("Mesh", GeometrySet()); - return; - } - - if (!profile_set.has_curve() && !profile_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in the profile input")); - } + const bool fill_caps = params.extract_input<bool>("Fill Caps"); bool has_curve = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_curve()) { has_curve = true; - geometry_set_curve_to_mesh(geometry_set, profile_set); + geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); - if (!has_curve && !curve_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in curve input")); - } - params.set_output("Mesh", std::move(curve_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 4f4fde6c7df..38d7fb99e87 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -30,13 +30,13 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000); - b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Points"); - b.add_output<decl::Vector>("Tangent").field_source(); - b.add_output<decl::Vector>("Normal").field_source(); - b.add_output<decl::Vector>("Rotation").field_source(); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000); + b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Points")); + b.add_output<decl::Vector>(N_("Tangent")).field_source(); + b.add_output<decl::Vector>(N_("Normal")).field_source(); + b.add_output<decl::Vector>(N_("Rotation")).field_source(); } static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -357,17 +357,20 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) if (attribute_outputs.tangent_id) { params.set_output( "Tangent", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id), + params.attribute_producer_name())); } if (attribute_outputs.normal_id) { params.set_output( "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } if (attribute_outputs.rotation_id) { params.set_output( "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 6b049b4d384..4e1a2910c7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -28,21 +28,24 @@ namespace blender::nodes { static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Float>("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input<decl::Float>("End") + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Float>(N_("Start")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input<decl::Float>(N_("End")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input<decl::Float>("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE).supports_field(); - b.add_input<decl::Float>("End", "End_001") + b.add_input<decl::Float>(N_("Start"), "Start_001") + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_input<decl::Float>(N_("End"), "End_001") .min(0.0f) .default_value(1.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index e293cd9b8fe..e0a3faaefb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -50,13 +50,13 @@ namespace blender::nodes { static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")) .default_value(true) .hide_value() .supports_field() - .description("The parts of the geometry to be deleted"); - b.add_output<decl::Geometry>("Geometry"); + .description(N_("The parts of the geometry to be deleted")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_delete_geometry_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 0d481011f00..fa439b04da0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -42,22 +42,22 @@ namespace blender::nodes { static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f); - b.add_input<decl::Float>("Density").default_value(10.0f).supports_field(); - b.add_input<decl::Float>("Density Factor") + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f); + b.add_input<decl::Float>(N_("Density")).default_value(10.0f).supports_field(); + b.add_input<decl::Float>(N_("Density Factor")) .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input<decl::Int>("Seed"); + b.add_input<decl::Int>(N_("Seed")); - b.add_output<decl::Geometry>("Points"); - b.add_output<decl::Vector>("Normal").field_source(); - b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source(); + b.add_output<decl::Geometry>(N_("Points")); + b.add_output<decl::Vector>(N_("Normal")).field_source(); + b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_source(); } static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, @@ -533,7 +533,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); const GeometryNodeDistributePointsOnFacesMode method = static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1); @@ -543,10 +543,10 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par AttributeOutputs attribute_outputs; if (params.output_is_required("Normal")) { - attribute_outputs.normal_id = StrongAnonymousAttributeID("normal"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); } if (params.output_is_required("Rotation")) { - attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -562,12 +562,14 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par if (attribute_outputs.normal_id) { params.set_output( "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } if (attribute_outputs.rotation_id) { params.set_output( "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index e97fc5c2c83..f562fb29e90 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -26,9 +26,9 @@ namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Mesh")); } static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc new file mode 100644 index 00000000000..e1c72fbd438 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -0,0 +1,429 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_geometry_util.hh" + +#include "BKE_image.h" + +#include "BLI_float4.hh" +#include "BLI_threads.h" +#include "BLI_timeit.hh" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_image_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Image>(N_("Image")).hide_label(); + b.add_input<decl::Vector>(N_("Vector")) + .implicit_field() + .description(("Texture coordinates from 0 to 1")); + b.add_input<decl::Int>(N_("Frame")).min(0).max(MAXFRAMEF); + b.add_output<decl::Color>(N_("Color")).no_muted_links().dependent_field(); + b.add_output<decl::Float>(N_("Alpha")).no_muted_links().dependent_field(); +} + +static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void geo_node_image_texture_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN( + sizeof(NodeGeometryImageTexture), __func__); + node->storage = tex; +} + +class ImageFieldsFunction : public fn::MultiFunction { + private: + const int interpolation_; + const int extension_; + Image &image_; + ImageUser image_user_; + void *image_lock_; + ImBuf *image_buffer_; + + public: + ImageFieldsFunction(const int interpolation, + const int extension, + Image &image, + ImageUser image_user) + : interpolation_(interpolation), + extension_(extension), + image_(image), + image_user_(image_user) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + + image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_); + if (image_buffer_ == nullptr) { + throw std::runtime_error("cannot aquire image buffer"); + } + + if (image_buffer_->rect_float == nullptr) { + BLI_thread_lock(LOCK_IMAGE); + if (!image_buffer_->rect_float) { + IMB_float_from_rect(image_buffer_); + } + BLI_thread_unlock(LOCK_IMAGE); + } + + if (image_buffer_->rect_float == nullptr) { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + throw std::runtime_error("cannot get float buffer"); + } + } + + ~ImageFieldsFunction() override + { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"ImageFunction"}; + signature.single_input<float3>("Vector"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + static int wrap_periodic(int x, const int width) + { + x %= width; + if (x < 0) { + x += width; + } + return x; + } + + static int wrap_clamp(const int x, const int width) + { + return std::clamp(x, 0, width - 1); + } + + static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py) + { + if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x]; + } + + static float frac(const float x, int *ix) + { + const int i = (int)x - ((x < 0.0f) ? 1 : 0); + *ix = i; + return x - (float)i; + } + + static float4 image_cubic_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float tx = frac(px * (float)width - 0.5f, &pix); + const float ty = frac(py * (float)height - 0.5f, &piy); + int ppix, ppiy, nnix, nniy; + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + ppix = wrap_periodic(pix - 1, width); + ppiy = wrap_periodic(piy - 1, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + nnix = wrap_periodic(pix + 2, width); + nniy = wrap_periodic(piy + 2, height); + break; + } + case SHD_IMAGE_EXTENSION_CLIP: { + ppix = pix - 1; + ppiy = piy - 1; + nix = pix + 1; + niy = piy + 1; + nnix = pix + 2; + nniy = piy + 2; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ppix = wrap_clamp(pix - 1, width); + ppiy = wrap_clamp(piy - 1, height); + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + nnix = wrap_clamp(pix + 2, width); + nniy = wrap_clamp(piy + 2, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + const int xc[4] = {ppix, pix, nix, nnix}; + const int yc[4] = {ppiy, piy, niy, nniy}; + float u[4], v[4]; + + u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f); + u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f); + u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f); + u[3] = (1.0f / 6.0f) * tx * tx * tx; + + v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f); + v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f); + v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f); + v[3] = (1.0f / 6.0f) * ty * ty * ty; + + return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) + + (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) + + (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) + + (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3])))); + } + + static float4 image_linear_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float nfx = frac(px * (float)width - 0.5f, &pix); + const float nfy = frac(py * (float)height - 0.5f, &piy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_CLIP: { + nix = pix + 1; + niy = piy + 1; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + case SHD_IMAGE_EXTENSION_REPEAT: + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + break; + } + + const float ptx = 1.0f - nfx; + const float pty = 1.0f - nfy; + + return image_pixel_lookup(ibuf, pix, piy) * ptx * pty + + image_pixel_lookup(ibuf, nix, piy) * nfx * pty + + image_pixel_lookup(ibuf, pix, niy) * ptx * nfy + + image_pixel_lookup(ibuf, nix, niy) * nfx * nfy; + } + + static float4 image_closest_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int ix, iy; + const float tx = frac(px * (float)width - 0.5f, &ix); + const float ty = frac(py * (float)height - 0.5f, &iy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + ix = wrap_periodic(ix, width); + iy = wrap_periodic(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + case SHD_IMAGE_EXTENSION_CLIP: { + if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + if (ix < 0 || iy < 0 || ix > width || iy > height) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + ATTR_FALLTHROUGH; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ix = wrap_clamp(ix, width); + iy = wrap_clamp(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector"); + MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>( + 1, "Color"); + MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha"); + + MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()}; + + /* Sample image texture. */ + switch (interpolation_) { + case SHD_INTERP_LINEAR: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CLOSEST: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CUBIC: + case SHD_INTERP_SMART: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + } + + int alpha_mode = image_.alpha_mode; + if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) { + alpha_mode = IMA_ALPHA_CHANNEL_PACKED; + } + + switch (alpha_mode) { + case IMA_ALPHA_STRAIGHT: { + /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */ + for (int64_t i : mask) { + straight_to_premul_v4(color_data[i]); + } + break; + } + case IMA_ALPHA_PREMUL: { + /* Alpha is premultiplied already, nothing to do. */ + break; + } + case IMA_ALPHA_CHANNEL_PACKED: { + /* Color and alpha channels shouldn't interact with each other, nothing to do. */ + break; + } + case IMA_ALPHA_IGNORE: { + /* The image should be treated as being opaque. */ + for (int64_t i : mask) { + color_data[i].w = 1.0f; + } + break; + } + } + + if (!r_alpha.is_empty()) { + for (int64_t i : mask) { + r_alpha[i] = r_color[i].a; + } + } + } +}; + +static void geo_node_image_texture_exec(GeoNodeExecParams params) +{ + auto return_default = [&]() { + params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + params.set_output("Alpha", 1.0f); + }; + + Image *image = params.get_input<Image *>("Image"); + if (image == nullptr) { + return return_default(); + } + + const bNode &node = params.node(); + NodeGeometryImageTexture *data = (NodeGeometryImageTexture *)node.storage; + + ImageUser image_user; + BKE_imageuser_default(&image_user); + image_user.cycl = false; + image_user.frames = INT_MAX; + image_user.sfra = 1; + image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0; + + std::unique_ptr<ImageFieldsFunction> image_fn; + try { + image_fn = std::make_unique<ImageFieldsFunction>( + data->interpolation, data->extension, *image, image_user); + } + catch (const std::runtime_error &) { + return return_default(); + } + + Field<float3> vector_field = params.extract_input<Field<float3>>("Vector"); + + auto image_op = std::make_shared<FieldOperation>( + FieldOperation(std::move(image_fn), {std::move(vector_field)})); + + params.set_output("Color", Field<ColorGeometry4f>(image_op, 0)); + params.set_output("Alpha", Field<float>(image_op, 1)); +} + +} // namespace blender::nodes + +void register_node_type_geo_image_texture(void) +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::geo_node_image_texture_declare; + ntype.draw_buttons = blender::nodes::geo_node_image_texture_layout; + node_type_init(&ntype, blender::nodes::geo_node_image_texture_init); + node_type_storage( + &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = blender::nodes::geo_node_image_texture_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index 604b181918d..b8df545d073 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -20,8 +20,8 @@ namespace blender::nodes { static void geo_node_input_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Left").field_source(); - b.add_output<decl::Vector>("Right").field_source(); + b.add_output<decl::Vector>(N_("Left")).field_source(); + b.add_output<decl::Vector>(N_("Right")).field_source(); } static void geo_node_input_curve_handles_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc index 5a24b7f3f07..f32db3842db 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Tilt").field_source(); + b.add_output<decl::Float>(N_("Tilt")).field_source(); } static void geo_node_input_curve_tilt_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc new file mode 100644 index 00000000000..37d5bac0325 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_id_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("ID")).field_source(); +} + +static void geo_node_input_id_exec(GeoNodeExecParams params) +{ + Field<int> position_field{std::make_shared<bke::IDAttributeFieldInput>()}; + params.set_output("ID", std::move(position_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_id_exec; + ntype.declare = blender::nodes::geo_node_input_id_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index 7fcbaf429dd..6200ac5e7a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_index_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>("Index").field_source(); + b.add_output<decl::Int>(N_("Index")).field_source(); } static void geo_node_input_index_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 8e805bd1359..fc41188dee5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_input_material_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Material>("Material"); + b.add_output<decl::Material>(N_("Material")); } static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc index 702c83daea0..5d5d9e40032 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_material_index_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>("Material Index").field_source(); + b.add_output<decl::Int>(N_("Material Index")).field_source(); } static void geo_node_input_material_index_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 5a2495afb9e..92b89313d23 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Normal").field_source(); + b.add_output<decl::Vector>(N_("Normal")).field_source(); } static GVArrayPtr mesh_face_normals(const Mesh &mesh, @@ -241,8 +241,9 @@ static const GVArray *construct_curve_normal_gvarray(const CurveComponent &compo class NormalFieldInput final : public fn::FieldInput { public: - NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal") + NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index 44874259e20..a8477d4bc4f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_position_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Position").field_source(); + b.add_output<decl::Vector>(N_("Position")).field_source(); } static void geo_node_input_position_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc index 586005511ad..6d2c4c38cbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_radius_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Radius").default_value(1.0f).min(0.0f).field_source(); + b.add_output<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).field_source(); } static void geo_node_input_radius_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc index de520787e78..dcd14b1c054 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_shade_smooth_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Smooth").field_source(); + b.add_output<decl::Bool>(N_("Smooth")).field_source(); } static void geo_node_input_shade_smooth_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc index 44a1bb62de8..a8ee6dd8b12 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_spline_cyclic_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Cyclic").field_source(); + b.add_output<decl::Bool>(N_("Cyclic")).field_source(); } static void geo_node_input_spline_cyclic_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index b5f3e1b0c28..895efa6f0ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Length").field_source(); + b.add_output<decl::Float>(N_("Length")).field_source(); } static const GVArray *construct_spline_length_gvarray(const CurveComponent &component, @@ -57,8 +57,9 @@ static const GVArray *construct_spline_length_gvarray(const CurveComponent &comp class SplineLengthFieldInput final : public fn::FieldInput { public: - SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length") + SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc index eab95ebc46e..75fb8a13d38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_spline_resolution_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>("Resolution").field_source(); + b.add_output<decl::Int>(N_("Resolution")).field_source(); } static void geo_node_input_spline_resolution_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index d690642373a..6b1736fe2ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Tangent").field_source(); + b.add_output<decl::Vector>(N_("Tangent")).field_source(); } static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents) @@ -121,8 +121,9 @@ static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &comp class TangentFieldInput final : public fn::FieldInput { public: - TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent") + TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 78399fad2c0..aff29d973d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -28,28 +28,29 @@ namespace blender::nodes { static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Points").description("Points to instance on"); - b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value(); - b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points"); - b.add_input<decl::Bool>("Pick Instance") + b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Geometry>(N_("Instance")) + .description(N_("Geometry that is instanced on the points")); + b.add_input<decl::Bool>(N_("Pick Instance")) .supports_field() .description("Place different instances on different points"); - b.add_input<decl::Int>("Instance Index") + b.add_input<decl::Int>(N_("Instance Index")) .implicit_field() - .description( + .description(N_( "Index of the instance that used for each point. This is only used when Pick Instances " - "is on. By default the point index is used"); - b.add_input<decl::Vector>("Rotation") + "is on. By default the point index is used")); + b.add_input<decl::Vector>(N_("Rotation")) .subtype(PROP_EULER) .supports_field() - .description("Rotation of the instances"); - b.add_input<decl::Vector>("Scale") + .description(N_("Rotation of the instances")); + b.add_input<decl::Vector>(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ) .supports_field() - .description("Scale of the instances"); + .description(N_("Scale of the instances")); - b.add_output<decl::Geometry>("Instances"); + b.add_output<decl::Geometry>(N_("Instances")); } static void add_instances_from_component(InstancesComponent &dst_component, @@ -77,7 +78,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, select_len); MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len, select_len); - MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, select_len); FieldEvaluator field_evaluator{field_context, domain_size}; const VArray<bool> *pick_instance = nullptr; @@ -86,7 +86,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, const VArray<float3> *scales = nullptr; /* The evaluator could use the component's stable IDs as a destination directly, but only the * selected indices should be copied. */ - GVArray_Typed<int> stable_ids = src_component.attribute_get_for_read("id", ATTR_DOMAIN_POINT, 0); field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance); field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices); field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations); @@ -119,7 +118,6 @@ static void add_instances_from_component(InstancesComponent &dst_component, threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { for (const int range_i : selection_range) { const int64_t i = selection[range_i]; - dst_stable_ids[range_i] = (*stable_ids)[i]; /* Compute base transform for every instances. */ float4x4 &dst_transform = dst_transforms[range_i]; @@ -157,6 +155,17 @@ static void add_instances_from_component(InstancesComponent &dst_component, } }); + GVArrayPtr id_attribute = src_component.attribute_try_get_for_read( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (id_attribute) { + GVArray_Typed<int> ids{*id_attribute}; + VArray_Span<int> ids_span{ids}; + MutableSpan<int> dst_ids = dst_component.instance_ids_ensure(); + for (const int64_t i : selection.index_range()) { + dst_ids[i] = ids_span[selection[i]]; + } + } + if (pick_instance->is_single()) { if (pick_instance->get_internal_single()) { if (instance.has_realized_data()) { @@ -194,11 +203,15 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params); geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); } - /* Unused references may have been added above. Remove those now so that other nodes don't - * process them needlessly. */ - instances.remove_unused_references(); }); + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. + * This should eventually be moved into the loop above, but currently this is quite tricky + * because it might remove references that the loop still wants to iterate over. */ + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + instances.remove_unused_references(); + params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 63d1f88a442..c3955426e69 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -23,15 +23,15 @@ namespace blender::nodes { static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Instances"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Float>(N_("Radius")) .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output<decl::Geometry>("Points"); + b.add_output<decl::Geometry>(N_("Points")); } template<typename T> @@ -77,13 +77,15 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const VArray<float> &radii = evaluator.get_evaluated<float>(1); copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); - OutputAttribute_Typed<int> id_attribute = points.attribute_try_get_for_output<int>( - "id", ATTR_DOMAIN_POINT, 0); - MutableSpan<int> ids = id_attribute.as_span(); - for (const int i : selection.index_range()) { - ids[i] = instances.instance_ids()[selection[i]]; + if (!instances.instance_ids().is_empty()) { + OutputAttribute_Typed<int> id_attribute = points.attribute_try_get_for_output<int>( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + MutableSpan<int> ids = id_attribute.as_span(); + for (const int i : selection.index_range()) { + ids[i] = instances.instance_ids()[selection[i]]; + } + id_attribute.save(); } - id_attribute.save(); } static void geo_node_instances_to_points_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc index f8a1c764f61..8e0e98f7bd5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Is Viewport"); + b.add_output<decl::Bool>(N_("Is Viewport")); } static void geo_node_is_viewport_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index b628c5cbab8..cd385f364e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -33,8 +33,8 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry").multi_input(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).multi_input(); + b.add_output<decl::Geometry>(N_("Geometry")); } static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components) @@ -270,17 +270,16 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo } Span<float4x4> src_transforms = src_component->instance_transforms(); - Span<int> src_ids = src_component->instance_ids(); Span<int> src_reference_handles = src_component->instance_reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - const int id = src_ids[i]; - dst_component.add_instance(dst_handle, transform, id); + dst_component.add_instance(dst_handle, transform); } } + join_attributes(to_base_components(src_components), dst_component, {"position"}); } static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index f3562bed6e9..e4a62bd5267 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Old"); - b.add_input<decl::Material>("New"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Material>(N_("Old")); + b.add_input<decl::Material>(N_("New")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_material_replace_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 9d4533b9bda..06c770820ee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -30,8 +30,8 @@ namespace blender::nodes { static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Material>("Material").hide_label(true); - b.add_output<decl::Bool>("Selection").field_source(); + b.add_input<decl::Material>(N_("Material")).hide_label(true); + b.add_output<decl::Bool>(N_("Selection")).field_source(); } static void select_mesh_by_material(const Mesh &mesh, @@ -59,8 +59,9 @@ class MaterialSelectionFieldInput final : public fn::FieldInput { public: MaterialSelectionFieldInput(Material *material) - : fn::FieldInput(CPPType::get<bool>(), "Material Selection"), material_(material) + : fn::FieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, 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 9c477c639a2..685a8faff5c 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,9 +29,9 @@ namespace blender::nodes { static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Vertices").default_value(32).min(3); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + 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_output<decl::Geometry>(N_("Mesh")); } static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, @@ -204,7 +204,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) const int verts_num = params.extract_input<int>("Vertices"); if (verts_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -212,7 +212,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 93c35a1111b..206d48d40c8 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 @@ -29,13 +29,16 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(512); - b.add_input<decl::Int>("Side Segments").default_value(1).min(1).max(512); - b.add_input<decl::Int>("Fill Segments").default_value(1).min(1).max(512); - b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + 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::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); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -708,14 +711,14 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -723,7 +726,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -737,7 +740,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 5a520d36296..3a211993bdc 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 @@ -26,11 +26,14 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); - b.add_input<decl::Int>("Vertices X").default_value(2).min(2).max(1000); - b.add_input<decl::Int>("Vertices Y").default_value(2).min(2).max(1000); - b.add_input<decl::Int>("Vertices Z").default_value(2).min(2).max(1000); - b.add_output<decl::Geometry>("Geometry"); + 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); + b.add_output<decl::Geometry>(N_("Mesh")); } struct CuboidConfig { @@ -476,13 +479,13 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const int verts_z = params.extract_input<int>("Vertices Z"); if (verts_x < 1 || verts_y < 1 || verts_z < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 287ea896ade..3bcf42b40b1 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 @@ -29,32 +29,32 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Vertices") + b.add_input<decl::Int>(N_("Vertices")) .default_value(32) .min(3) .max(512) - .description("The number of vertices around the circumference"); - b.add_input<decl::Int>("Side Segments") + .description(N_("The number of vertices around the circumference")); + b.add_input<decl::Int>(N_("Side Segments")) .default_value(1) .min(1) .max(512) - .description("The number of segments along the side"); - b.add_input<decl::Int>("Fill Segments") + .description(N_("The number of segments along the side")); + b.add_input<decl::Int>(N_("Fill Segments")) .default_value(1) .min(1) .max(512) - .description("The number of concentric segments of the fill"); - b.add_input<decl::Float>("Radius") + .description(N_("The number of concentric segments of the fill")); + b.add_input<decl::Float>(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The radius of the cylinder"); - b.add_input<decl::Float>("Depth") + .description(N_("The radius of the cylinder")); + b.add_input<decl::Float>(N_("Depth")) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The height of the cylinder on the Z axis"); - b.add_output<decl::Geometry>("Geometry"); + .description(N_("The height of the cylinder on the Z axis")); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, @@ -102,14 +102,14 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -117,7 +117,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) 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("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -125,7 +125,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) Mesh *mesh = create_cylinder_or_cone_mesh( radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 858ef8648f8..c4e476981c1 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,11 +29,11 @@ namespace blender::nodes { static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>("Vertices X").default_value(3).min(2).max(1000); - b.add_input<decl::Int>("Vertices Y").default_value(3).min(2).max(1000); - b.add_output<decl::Geometry>("Geometry"); + 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_output<decl::Geometry>(N_("Mesh")); } static void calculate_uvs( @@ -160,7 +160,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) const int verts_x = params.extract_input<int>("Vertices X"); const int verts_y = params.extract_input<int>("Vertices Y"); if (verts_x < 1 || verts_y < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -168,7 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 5ea7165ac31..da3dfef3aea 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,9 +28,9 @@ namespace blender::nodes { static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>("Subdivisions").default_value(1).min(1).max(7); - b.add_output<decl::Geometry>("Geometry"); + 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_output<decl::Geometry>(N_("Mesh")); } static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) @@ -66,7 +66,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 031223b5ca6..6515afe5966 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,11 +29,13 @@ namespace blender::nodes { static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Count").default_value(10).min(1).max(10000); - b.add_input<decl::Float>("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Start Location").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Geometry"); + 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::Vector>(N_("Offset")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_mesh_primitive_line_layout(uiLayout *layout, @@ -154,7 +156,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) mesh = create_line_mesh(start, delta, count); } - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes 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 6fd6cdf5747..54a762fc15d 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,10 +29,10 @@ namespace blender::nodes { static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Segments").default_value(32).min(3).max(1024); - b.add_input<decl::Int>("Rings").default_value(16).min(2).max(1024); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + 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_output<decl::Geometry>(N_("Mesh")); } static int sphere_vert_total(const int segments, const int rings) @@ -291,14 +291,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) if (rings_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); } - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index d1dd5b1bf8b..d99c0c851a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) @@ -74,12 +74,12 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; #endif @@ -87,14 +87,14 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11); if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 7bca9ec141b..11865c635b8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -22,9 +22,9 @@ namespace blender::nodes { static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index df0fdd8eccd..92911e89f59 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -30,15 +30,15 @@ namespace blender::nodes { static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value(); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Float>(N_("Radius")) .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output<decl::Geometry>("Points"); + b.add_output<decl::Geometry>(N_("Points")); } static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index e61709ed86a..3ba32c4b674 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -25,15 +25,15 @@ namespace blender::nodes { static void geo_node_object_info_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Object>("Object").hide_label(); - b.add_input<decl::Bool>("As Instance") + b.add_input<decl::Object>(N_("Object")).hide_label(); + b.add_input<decl::Bool>(N_("As Instance")) .description( - "Output the entire object as single instance. " - "This allows instancing non-geometry object types"); - b.add_output<decl::Vector>("Location"); - b.add_output<decl::Vector>("Rotation"); - b.add_output<decl::Vector>("Scale"); - b.add_output<decl::Geometry>("Geometry"); + N_("Output the entire object as single instance. " + "This allows instancing non-geometry object types")); + b.add_output<decl::Vector>(N_("Location")); + b.add_output<decl::Vector>(N_("Rotation")); + b.add_output<decl::Vector>(N_("Scale")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index afd0ced6360..3e0096824d3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Points"); - b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value(); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_output<decl::Geometry>(N_("Mesh")); } template<typename T> 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 719523f64f0..312ea7df919 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 @@ -32,16 +32,16 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f); - b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Points")); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Radius")) .default_value(0.5f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>(N_("Volume")); } static void geo_node_points_to_volume_layout(uiLayout *layout, @@ -239,17 +239,17 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, static void geo_node_points_to_volume_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); #ifdef WITH_OPENVDB geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { initialize_volume_component_from_points(params, geometry_set); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Volume", std::move(geometry_set)); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Volume", GeometrySet()); #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index ec4d6ceb728..c05476b982b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -31,10 +31,12 @@ namespace blender::nodes { static void geo_node_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Target"); - b.add_input<decl::Vector>("Source Position").implicit_field(); - b.add_output<decl::Vector>("Position").dependent_field(); - b.add_output<decl::Float>("Distance").dependent_field(); + b.add_input<decl::Geometry>(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_output<decl::Vector>(N_("Position")).dependent_field(); + b.add_output<decl::Float>(N_("Distance")).dependent_field(); } static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -214,12 +216,6 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) params.set_output("Distance", fn::make_constant_field<float>(0.0f)); }; - if (geometry_set_target.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized mesh or point cloud data, instances are ignored")); - } - if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { return return_default(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1e687f163e6..34946b1115c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -31,32 +31,36 @@ namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Target Geometry"); - - b.add_input<decl::Vector>("Attribute").hide_value().supports_field(); - b.add_input<decl::Float>("Attribute", "Attribute_001").hide_value().supports_field(); - b.add_input<decl::Color>("Attribute", "Attribute_002").hide_value().supports_field(); - b.add_input<decl::Bool>("Attribute", "Attribute_003").hide_value().supports_field(); - b.add_input<decl::Int>("Attribute", "Attribute_004").hide_value().supports_field(); - - b.add_input<decl::Vector>("Source Position").implicit_field(); - b.add_input<decl::Vector>("Ray Direction").default_value({0.0f, 0.0f, 1.0f}).supports_field(); - b.add_input<decl::Float>("Ray Length") + b.add_input<decl::Geometry>(N_("Target Geometry")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + + b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Ray Direction")) + .default_value({0.0f, 0.0f, -1.0f}) + .supports_field(); + b.add_input<decl::Float>(N_("Ray Length")) .default_value(100.0f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_output<decl::Bool>("Is Hit").dependent_field(); - b.add_output<decl::Vector>("Hit Position").dependent_field(); - b.add_output<decl::Vector>("Hit Normal").dependent_field(); - b.add_output<decl::Float>("Hit Distance").dependent_field(); + b.add_output<decl::Bool>(N_("Is Hit")).dependent_field(); + b.add_output<decl::Vector>(N_("Hit Position")).dependent_field(); + b.add_output<decl::Vector>(N_("Hit Normal")).dependent_field(); + b.add_output<decl::Float>(N_("Hit Distance")).dependent_field(); - b.add_output<decl::Vector>("Attribute").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output<decl::Float>("Attribute", "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output<decl::Color>("Attribute", "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output<decl::Bool>("Attribute", "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); - b.add_output<decl::Int>("Attribute", "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Vector>(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); } static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -389,26 +393,11 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) }); }; - if (target.has_instances()) { - if (target.has_realized_data()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("The node only supports realized mesh data, instances are ignored")); - } - else { - params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain realized data")); - return return_default(); - } - } - if (target.is_empty()) { return return_default(); } if (!target.has_mesh()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The target geometry must contain a mesh")); return return_default(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 3be79d5ba3b..6c51c1f738f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -23,8 +23,8 @@ namespace blender::nodes { static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void geo_node_realize_instances_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 09d92d7fa1e..abf44b1aaf8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -22,12 +22,12 @@ namespace blender::nodes { static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).supports_field(); - b.add_input<decl::Vector>("Pivot Point").subtype(PROP_TRANSLATION).supports_field(); - b.add_input<decl::Bool>("Local Space").default_value(true).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).supports_field(); + b.add_input<decl::Vector>(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); }; static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -55,34 +55,55 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; + const float3 euler = rotations[i]; float4x4 &instance_transform = instance_transforms[i]; - const float4x4 rotation_matrix = float4x4::from_loc_eul_scale( - {0, 0, 0}, rotations[i], {1, 1, 1}); + + float4x4 rotation_matrix; + float3 used_pivot; if (local_spaces[i]) { - instance_transform *= float4x4::from_location(pivot); - instance_transform *= rotation_matrix; - instance_transform *= float4x4::from_location(-pivot); + /* Find rotation axis from the matrix. This should work even if the instance is skewed. */ + const float3 rotation_axis_x = instance_transform.values[0]; + const float3 rotation_axis_y = instance_transform.values[1]; + const float3 rotation_axis_z = instance_transform.values[2]; + + /* Create rotations around the individual axis. This could be optimized to skip some axis + * when the angle is zero. */ + float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3]; + axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x); + axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y); + axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z); + + /* Combine the previously computed rotations into the final rotation matrix. */ + float rotation[3][3]; + mul_m3_series(rotation, rotation_z, rotation_y, rotation_x); + copy_m4_m3(rotation_matrix.values, rotation); + + /* Transform the passed in pivot into the local space of the instance. */ + used_pivot = instance_transform * pivot; } else { - const float4x4 orgiginal_transform = instance_transform; - instance_transform = float4x4::from_location(pivot); - instance_transform *= rotation_matrix; - instance_transform *= float4x4::from_location(-pivot); - instance_transform *= orgiginal_transform; + used_pivot = pivot; + eul_to_mat4(rotation_matrix.values, euler); } + /* Move the pivot to the origin so that we can rotate around it. */ + sub_v3_v3(instance_transform.values[3], used_pivot); + /* Perform the actual rotation. */ + mul_m4_m4_pre(instance_transform.values, rotation_matrix.values); + /* Undo the pivot shifting done before. */ + add_v3_v3(instance_transform.values[3], used_pivot); } }); } static void geo_node_rotate_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); rotate_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 33897ef354d..ea2b458410e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -22,12 +22,15 @@ namespace blender::nodes { static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Scale").subtype(PROP_XYZ).default_value({1, 1, 1}).supports_field(); - b.add_input<decl::Vector>("Center").subtype(PROP_TRANSLATION).supports_field(); - b.add_input<decl::Bool>("Local Space").default_value(true).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Scale")) + .subtype(PROP_XYZ) + .default_value({1, 1, 1}) + .supports_field(); + b.add_input<decl::Vector>(N_("Center")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); }; static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -74,12 +77,12 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta static void geo_node_scale_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); scale_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index dafd10cee2d..a16fb712b13 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -20,12 +20,12 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Mesh"); - b.add_output<decl::Geometry>("Point Cloud"); - b.add_output<decl::Geometry>("Curve"); - b.add_output<decl::Geometry>("Volume"); - b.add_output<decl::Geometry>("Instances"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Geometry>(N_("Point Cloud")); + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Geometry>(N_("Volume")); + b.add_output<decl::Geometry>(N_("Instances")); } static void geo_node_separate_components_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc index 970d49e0626..28e214c0ccc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -23,16 +23,16 @@ namespace blender::nodes { static void geo_node_separate_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")) .default_value(true) .hide_value() .supports_field() - .description("The parts of the geometry that go into the first output"); - b.add_output<decl::Geometry>("Selection") - .description("The parts of the geometry in the selection"); - b.add_output<decl::Geometry>("Inverted") - .description("The parts of the geometry not in the selection"); + .description(N_("The parts of the geometry that go into the first output")); + b.add_output<decl::Geometry>(N_("Selection")) + .description(N_("The parts of the geometry in the selection")); + b.add_output<decl::Geometry>(N_("Inverted")) + .description(N_("The parts of the geometry not in the selection")); } static void geo_node_separate_geometry_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index d7aaaffc7c6..b64aa266330 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -25,10 +25,10 @@ namespace blender::nodes { static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void geo_node_set_curve_handles_layout(uiLayout *layout, @@ -125,7 +125,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) (NodeGeometrySetCurveHandlePositions *)params.node().storage; const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); Field<float3> position_field = params.extract_input<Field<float3>>("Position"); @@ -144,7 +144,7 @@ static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Info, TIP_("The input geometry does not contain a Bezier spline")); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 8fa4ff1a808..e47ce7dea30 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -20,11 +20,14 @@ namespace blender::nodes { static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Float>("Radius").min(0.0f).default_value(1.0f).supports_field().subtype( - PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Radius")) + .min(0.0f) + .default_value(1.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curve")); } static void set_radius_in_component(GeometryComponent &component, @@ -52,7 +55,7 @@ static void set_radius_in_component(GeometryComponent &component, static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); Field<float> radii_field = params.extract_input<Field<float>>("Radius"); @@ -63,7 +66,7 @@ static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 113149613ef..dde6d0bab92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Float>("Tilt").subtype(PROP_ANGLE).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Tilt")).subtype(PROP_ANGLE).supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } static void set_tilt_in_component(GeometryComponent &component, @@ -51,7 +51,7 @@ static void set_tilt_in_component(GeometryComponent &component, static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); @@ -62,7 +62,7 @@ static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc new file mode 100644 index 00000000000..77d8e786501 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_id_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("ID")).implicit_field(); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_id_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<int> &id_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + /* Since adding the ID attribute can change the result of the field evaluation (the random value + * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating + * the field. However, as an optimization, use a faster code path when it already exists. */ + fn::FieldEvaluator id_evaluator{field_context, &selection}; + if (component.attribute_exists("id")) { + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + id_evaluator.add_with_destination(id_field, id_attribute.varray()); + id_evaluator.evaluate(); + id_attribute.save(); + } + else { + id_evaluator.add(id_field); + id_evaluator.evaluate(); + const VArray<int> &result_ids = id_evaluator.get_evaluated<int>(0); + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + result_ids.materialize(selection, id_attribute.as_span()); + id_attribute.save(); + } +} + +static void geo_node_set_id_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<int> id_field = params.extract_input<Field<int>>("ID"); + + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(type)) { + set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_id_exec; + ntype.declare = blender::nodes::geo_node_set_id_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 0d85af60944..040509ad6d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Material>("Material").hide_label(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Material>(N_("Material")).hide_label(); + b.add_output<decl::Geometry>(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index a25fe332916..a8bb1bd8644 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Int>("Material Index").supports_field().min(0); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Material Index")).supports_field().min(0); + b.add_output<decl::Geometry>(N_("Geometry")); } static void set_material_index_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index b59c9a9e8f5..9ff299542b4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -20,11 +20,14 @@ namespace blender::nodes { static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Float>("Radius").default_value(0.05f).min(0.0f).supports_field().subtype( - PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Points")); } static void set_radius_in_component(GeometryComponent &component, @@ -52,7 +55,7 @@ static void set_radius_in_component(GeometryComponent &component, static void geo_node_set_point_radius_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); Field<float> radii_field = params.extract_input<Field<float>>("Radius"); @@ -64,7 +67,7 @@ static void geo_node_set_point_radius_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Points", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 15930508e78..4e564386a28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -22,17 +22,17 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_input<decl::Bool>("Offset").default_value(false).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Offset")).supports_field().subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>(N_("Geometry")); } static void set_position_in_component(GeometryComponent &component, const Field<bool> &selection_field, const Field<float3> &position_field, - const Field<bool> &offset_field) + const Field<float3> &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -45,10 +45,6 @@ static void set_position_in_component(GeometryComponent &component, selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - MutableSpan<float3> position_mutable = positions.as_span(); - fn::FieldEvaluator position_evaluator{field_context, &selection}; position_evaluator.add(position_field); position_evaluator.add(offset_field); @@ -58,11 +54,14 @@ static void set_position_in_component(GeometryComponent &component, * value or not */ const VArray<float3> &positions_input = position_evaluator.get_evaluated<float3>(0); - const VArray<bool> &offsets_input = position_evaluator.get_evaluated<bool>(1); + const VArray<float3> &offsets_input = position_evaluator.get_evaluated<float3>(1); + + OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan<float3> position_mutable = positions.as_span(); for (int i : selection) { - position_mutable[i] = offsets_input[i] ? position_mutable[i] + positions_input[i] : - positions_input[i]; + position_mutable[i] = positions_input[i] + offsets_input[i]; } positions.save(); } @@ -71,7 +70,7 @@ static void geo_node_set_position_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - Field<bool> offset_field = params.extract_input<Field<bool>>("Offset"); + Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); Field<float3> position_field = params.extract_input<Field<float3>>("Position"); for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index ca77041ba7c..06e25c2ed55 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Bool>("Shade Smooth").supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Shade Smooth")).supports_field().default_value(true); + b.add_output<decl::Geometry>(N_("Geometry")); } static void set_smooth_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 50e00ff3758..ec751ae1d2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -20,10 +20,10 @@ namespace blender::nodes { static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Bool>("Cyclic").supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Cyclic")).supports_field(); + b.add_output<decl::Geometry>(N_("Geometry")); } static void set_cyclic_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index dccb0b1a969..ccf419975ca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -22,10 +22,10 @@ namespace blender::nodes { static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Int>("Resolution").default_value(12).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Resolution")).default_value(12).supports_field(); + b.add_output<decl::Geometry>(N_("Geometry")); } static void set_resolution_in_component(GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index 515f072e976..98d0aca084a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -20,9 +20,9 @@ namespace blender::nodes { static void geo_node_string_join_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("Delimiter"); - b.add_input<decl::String>("Strings").multi_input().hide_value(); - b.add_output<decl::String>("String"); + b.add_input<decl::String>(N_("Delimiter")); + b.add_input<decl::String>(N_("Strings")).multi_input().hide_value(); + b.add_output<decl::String>(N_("String")); }; static void geo_node_string_join_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 1cb6d43f685..95e94a22d81 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -34,18 +34,30 @@ namespace blender::nodes { static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("String"); - b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Character Spacing") + b.add_input<decl::String>(N_("String")); + b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Character Spacing")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curves"); - b.add_output<decl::String>("Remainder"); + b.add_input<decl::Float>(N_("Word Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Line Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Text Box Width")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Text Box Height")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curves")); + b.add_output<decl::String>(N_("Remainder")); } static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) @@ -242,13 +254,11 @@ static void add_instances_from_handles(InstancesComponent &instances, instances.resize(positions.size()); MutableSpan<int> handles = instances.instance_reference_handles(); MutableSpan<float4x4> transforms = instances.instance_transforms(); - MutableSpan<int> instance_ids = instances.instance_ids(); threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { for (const int i : range) { handles[i] = char_handles.lookup(charcodes[i]); transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); - instance_ids[i] = i; } }); } @@ -271,8 +281,9 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params) /* Convert UTF-8 encoded string to UTF-32. */ size_t len_bytes; size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); - Array<char32_t> char_codes(len_chars + 1); - BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1); + Array<char32_t> char_codes_with_null(len_chars + 1); + BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1); + const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1); /* Create and add instances. */ GeometrySet geometry_set_out; diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 410c9a8bb35..2b3430a5ed0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -31,29 +31,23 @@ namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); - b.add_input<decl::Float>("Crease") + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_input<decl::Float>(N_("Crease")) .default_value(0.0f) .min(0.0f) .max(1.0f) .supports_field() .subtype(PROP_FACTOR); - b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { -#ifdef WITH_OPENSUBDIV - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); - uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); -#else - UNUSED_VARS(layout, ptr); -#endif + uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE); } static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -67,7 +61,7 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); @@ -82,7 +76,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) /* Only process subdivision if level is greater than 0. */ if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } @@ -148,7 +142,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) BKE_subdiv_free(subdiv); }); #endif - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index c01fcf5bb5f..7e07a552650 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -30,48 +30,57 @@ namespace blender::nodes { static void geo_node_switch_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Bool>("Switch").default_value(false).supports_field(); - b.add_input<decl::Bool>("Switch", "Switch_001").default_value(false); - - b.add_input<decl::Float>("False").supports_field(); - b.add_input<decl::Float>("True").supports_field(); - b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000).supports_field(); - b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000).supports_field(); - b.add_input<decl::Bool>("False", "False_002").default_value(false).hide_value().supports_field(); - b.add_input<decl::Bool>("True", "True_002").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("False", "False_003").supports_field(); - b.add_input<decl::Vector>("True", "True_003").supports_field(); - b.add_input<decl::Color>("False", "False_004") + b.add_input<decl::Bool>(N_("Switch")).default_value(false).supports_field(); + b.add_input<decl::Bool>(N_("Switch"), "Switch_001").default_value(false); + + b.add_input<decl::Float>(N_("False")).supports_field(); + b.add_input<decl::Float>(N_("True")).supports_field(); + b.add_input<decl::Int>(N_("False"), "False_001").min(-100000).max(100000).supports_field(); + b.add_input<decl::Int>(N_("True"), "True_001").min(-100000).max(100000).supports_field(); + b.add_input<decl::Bool>(N_("False"), "False_002") + .default_value(false) + .hide_value() + .supports_field(); + b.add_input<decl::Bool>(N_("True"), "True_002") + .default_value(true) + .hide_value() + .supports_field(); + b.add_input<decl::Vector>(N_("False"), "False_003").supports_field(); + b.add_input<decl::Vector>(N_("True"), "True_003").supports_field(); + b.add_input<decl::Color>(N_("False"), "False_004") .default_value({0.8f, 0.8f, 0.8f, 1.0f}) .supports_field(); - b.add_input<decl::Color>("True", "True_004") + b.add_input<decl::Color>(N_("True"), "True_004") .default_value({0.8f, 0.8f, 0.8f, 1.0f}) .supports_field(); - b.add_input<decl::String>("False", "False_005").supports_field(); - b.add_input<decl::String>("True", "True_005").supports_field(); - - b.add_input<decl::Geometry>("False", "False_006"); - b.add_input<decl::Geometry>("True", "True_006"); - b.add_input<decl::Object>("False", "False_007"); - b.add_input<decl::Object>("True", "True_007"); - b.add_input<decl::Collection>("False", "False_008"); - b.add_input<decl::Collection>("True", "True_008"); - b.add_input<decl::Texture>("False", "False_009"); - b.add_input<decl::Texture>("True", "True_009"); - b.add_input<decl::Material>("False", "False_010"); - b.add_input<decl::Material>("True", "True_010"); - - b.add_output<decl::Float>("Output").dependent_field(); - b.add_output<decl::Int>("Output", "Output_001").dependent_field(); - b.add_output<decl::Bool>("Output", "Output_002").dependent_field(); - b.add_output<decl::Vector>("Output", "Output_003").dependent_field(); - b.add_output<decl::Color>("Output", "Output_004").dependent_field(); - b.add_output<decl::String>("Output", "Output_005").dependent_field(); - b.add_output<decl::Geometry>("Output", "Output_006"); - b.add_output<decl::Object>("Output", "Output_007"); - b.add_output<decl::Collection>("Output", "Output_008"); - b.add_output<decl::Texture>("Output", "Output_009"); - b.add_output<decl::Material>("Output", "Output_010"); + b.add_input<decl::String>(N_("False"), "False_005").supports_field(); + b.add_input<decl::String>(N_("True"), "True_005").supports_field(); + + b.add_input<decl::Geometry>(N_("False"), "False_006"); + b.add_input<decl::Geometry>(N_("True"), "True_006"); + b.add_input<decl::Object>(N_("False"), "False_007"); + b.add_input<decl::Object>(N_("True"), "True_007"); + b.add_input<decl::Collection>(N_("False"), "False_008"); + b.add_input<decl::Collection>(N_("True"), "True_008"); + b.add_input<decl::Texture>(N_("False"), "False_009"); + b.add_input<decl::Texture>(N_("True"), "True_009"); + b.add_input<decl::Material>(N_("False"), "False_010"); + b.add_input<decl::Material>(N_("True"), "True_010"); + b.add_input<decl::Image>(N_("False"), "False_011"); + b.add_input<decl::Image>(N_("True"), "True_011"); + + b.add_output<decl::Float>(N_("Output")).dependent_field(); + b.add_output<decl::Int>(N_("Output"), "Output_001").dependent_field(); + b.add_output<decl::Bool>(N_("Output"), "Output_002").dependent_field(); + b.add_output<decl::Vector>(N_("Output"), "Output_003").dependent_field(); + b.add_output<decl::Color>(N_("Output"), "Output_004").dependent_field(); + b.add_output<decl::String>(N_("Output"), "Output_005").dependent_field(); + b.add_output<decl::Geometry>(N_("Output"), "Output_006"); + b.add_output<decl::Object>(N_("Output"), "Output_007"); + b.add_output<decl::Collection>(N_("Output"), "Output_008"); + b.add_output<decl::Texture>(N_("Output"), "Output_009"); + b.add_output<decl::Material>(N_("Output"), "Output_010"); + b.add_output<decl::Image>(N_("Output"), "Output_011"); } static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -274,6 +283,10 @@ static void geo_node_switch_exec(GeoNodeExecParams params) switch_no_fields<Material *>(params, "_010"); break; } + case SOCK_IMAGE: { + switch_no_fields<Image *>(params, "_011"); + break; + } default: BLI_assert_unreachable(); break; diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 1c287a0f1bf..a889678537f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -40,22 +40,24 @@ namespace blender::nodes { static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Target"); - - b.add_input<decl::Vector>("Attribute").hide_value().supports_field(); - b.add_input<decl::Float>("Attribute", "Attribute_001").hide_value().supports_field(); - b.add_input<decl::Color>("Attribute", "Attribute_002").hide_value().supports_field(); - b.add_input<decl::Bool>("Attribute", "Attribute_003").hide_value().supports_field(); - b.add_input<decl::Int>("Attribute", "Attribute_004").hide_value().supports_field(); - - b.add_input<decl::Vector>("Source Position").implicit_field(); - b.add_input<decl::Int>("Index").implicit_field(); - - b.add_output<decl::Vector>("Attribute").dependent_field({6, 7}); - b.add_output<decl::Float>("Attribute", "Attribute_001").dependent_field({6, 7}); - b.add_output<decl::Color>("Attribute", "Attribute_002").dependent_field({6, 7}); - b.add_output<decl::Bool>("Attribute", "Attribute_003").dependent_field({6, 7}); - b.add_output<decl::Int>("Attribute", "Attribute_004").dependent_field({6, 7}); + b.add_input<decl::Geometry>(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + + b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_input<decl::Int>(N_("Index")).implicit_field(); + + b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7}); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); } static void geo_node_transfer_attribute_layout(uiLayout *layout, @@ -628,13 +630,14 @@ class IndexTransferFieldInput : public FieldInput { GField src_field, Field<int> index_field, const AttributeDomain domain) - : FieldInput(src_field.cpp_type(), "Attribute Transfer Index"), + : FieldInput(src_field.cpp_type(), "Attribute Transfer node"), src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), index_field_(std::move(index_field)), domain_(domain) { src_geometry_.ensure_owns_direct_data(); + category_ = Category::Generated; } const GVArray *get_varray_for_context(const FieldContext &context, @@ -746,21 +749,9 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) }); }; - if (geometry.has_instances()) { - if (geometry.has_realized_data()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("Only realized geometry is supported, instances will not be used")); - } - else { - params.error_message_add(NodeWarningType::Error, - TIP_("Target geometry must contain realized data")); - return return_default(); - } - /* Since the instances are not used, there is no point in keeping - * a reference to them while the field is passed around. */ - geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); - } + /* Since the instances are not used, there is no point in keeping + * a reference to them while the field is passed around. */ + geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); GField output_field; switch (mapping) { @@ -790,8 +781,6 @@ static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) } case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("Curve targets are not currently supported")); return return_default(); } auto fn = std::make_unique<NearestTransferFunction>( diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 005714a9580..2c55a255b5d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -37,11 +37,11 @@ namespace blender::nodes { static void geo_node_transform_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER); - b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ); + b.add_output<decl::Geometry>(N_("Geometry")); } static bool use_translate(const float3 rotation, const float3 scale) diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index 8fc2843fd8a..fa05d858a07 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -22,11 +22,11 @@ namespace blender::nodes { static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION).supports_field(); - b.add_input<decl::Bool>("Local Space").default_value(true).supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); }; static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) @@ -62,12 +62,12 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i static void geo_node_translate_instances_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); if (geometry_set.has_instances()) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); translate_instances(params, instances); } - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Instances", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 7ef0913622c..c869846e1f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -31,9 +31,9 @@ namespace blender::nodes { static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Minimum Vertices").default_value(4).min(4).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -50,7 +50,7 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) static void geo_node_triangulate_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4); GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>( @@ -67,7 +67,7 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params) } }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 3331962341f..194d1a751ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -14,13 +14,69 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_geometry_util.hh" namespace blender::nodes { static void geo_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Value")).supports_field().hide_value(); + b.add_input<decl::Vector>(N_("Value"), "Value_001").supports_field().hide_value(); + b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field().hide_value(); + b.add_input<decl::Int>(N_("Value"), "Value_003").supports_field().hide_value(); + b.add_input<decl::Bool>(N_("Value"), "Value_004").supports_field().hide_value(); +} + +static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer), + __func__); + data->data_type = CD_PROP_FLOAT; + + node->storage = data; +} + +static void geo_node_viewer_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); } + +static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return SOCK_FLOAT; + case CD_PROP_INT32: + return SOCK_INT; + case CD_PROP_FLOAT3: + return SOCK_VECTOR; + case CD_PROP_BOOL: + return SOCK_BOOLEAN; + case CD_PROP_COLOR: + return SOCK_RGBA; + default: + BLI_assert_unreachable(); + return SOCK_FLOAT; + } +} + +static void geo_node_viewer_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryViewer &storage = *(const NodeGeometryViewer *)node->storage; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_GEOMETRY) { + continue; + } + nodeSetSocketAvailability(socket, socket->type == socket_type); + } +} + } // namespace blender::nodes void register_node_type_geo_viewer() @@ -28,6 +84,11 @@ void register_node_type_geo_viewer() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); + node_type_storage( + &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, blender::nodes::geo_node_viewer_update); + node_type_init(&ntype, blender::nodes::geo_node_viewer_init); ntype.declare = blender::nodes::geo_node_viewer_declare; + ntype.draw_buttons_ex = blender::nodes::geo_node_viewer_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 229a35e0007..416d502dc59 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,13 +39,12 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Density"); - b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); - b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -60,11 +59,6 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( sizeof(NodeGeometryVolumeToMesh), __func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; - - bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - node->storage = data; } @@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node #ifdef WITH_OPENVDB -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) +static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) { - if (!geometry_set_in.has<VolumeComponent>()) { - return; - } - const NodeGeometryVolumeToMesh &storage = *(const NodeGeometryVolumeToMesh *)params.node().storage; bke::VolumeToMeshResolution resolution; resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { - resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } + resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f); } else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } + resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f); + } + + return resolution; +} + +static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids, + const float threshold, + const float adaptivity, + const bke::VolumeToMeshResolution &resolution) +{ + Array<bke::OpenVDBMeshData> mesh_data(grids.size()); + for (const int i : grids.index_range()) { + mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); + } + + int vert_offset = 0; + int poly_offset = 0; + int loop_offset = 0; + Array<int> vert_offsets(mesh_data.size()); + Array<int> poly_offsets(mesh_data.size()); + Array<int> loop_offsets(mesh_data.size()); + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + vert_offsets[i] = vert_offset; + poly_offsets[i] = poly_offset; + loop_offsets[i] = loop_offset; + vert_offset += data.verts.size(); + poly_offset += (data.tris.size() + data.quads.size()); + loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); } - const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); - const Volume *volume = component->get_for_read(); + Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + bke::fill_mesh_from_openvdb_data(data.verts, + data.tris, + data.quads, + vert_offsets[i], + poly_offsets[i], + loop_offsets[i], + verts, + polys, + loops); + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; +} + +static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) +{ + const Volume *volume = geometry_set.get_volume_for_read(); if (volume == nullptr) { - return; + return nullptr; } + const bke::VolumeToMeshResolution resolution = get_resolution_param(params); const Main *bmain = DEG_get_bmain(params.depsgraph()); BKE_volume_load(volume, bmain); - const std::string grid_name = params.get_input<std::string>("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; + Vector<openvdb::GridBase::ConstPtr> grids; + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + grids.append(std::move(grid)); } - float threshold = params.get_input<float>("Threshold"); - float adaptivity = params.get_input<float>("Adaptivity"); - - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); - if (mesh == nullptr) { - return; + if (grids.is_empty()) { + return nullptr; } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); - dst_component.replace(mesh); + + return create_mesh_from_volume_grids(grids, + params.get_input<float>("Threshold"), + params.get_input<float>("Adaptivity"), + resolution); } #endif /* WITH_OPENVDB */ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); #ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = create_mesh_from_volume(geometry_set, params); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - params.set_output("Geometry", geometry_set_out); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes @@ -163,7 +202,7 @@ void register_node_type_geo_volume_to_mesh() ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); - node_type_size(&ntype, 200, 120, 700); + node_type_size(&ntype, 170, 120, 700); node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; diff --git a/source/blender/nodes/intern/extern_implementations.cc b/source/blender/nodes/intern/extern_implementations.cc index 42d4b2878bc..35de319f20b 100644 --- a/source/blender/nodes/intern/extern_implementations.cc +++ b/source/blender/nodes/intern/extern_implementations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" namespace blender::nodes { #define MAKE_EXTERN_SOCKET_IMPLEMENTATION(TYPE) \ diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 852f52f38cd..ddd3c991518 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -21,9 +21,16 @@ #include "DNA_modifier_types.h" #include "DNA_space_types.h" +#include "FN_field_cpp_type.hh" + +#include "BLT_translation.h" + namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; +using fn::FieldCPPType; +using fn::FieldInput; +using fn::GField; ModifierLog::ModifierLog(GeoLogger &logger) : input_geometry_log_(std::move(logger.input_geometry_log_)), @@ -168,6 +175,34 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); } +GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) +{ + Set<std::reference_wrapper<const FieldInput>> field_inputs_set; + field.node().foreach_field_input( + [&](const FieldInput &field_input) { field_inputs_set.add(field_input); }); + + Vector<std::reference_wrapper<const FieldInput>> field_inputs; + field_inputs.extend(field_inputs_set.begin(), field_inputs_set.end()); + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + + for (const FieldInput &field_input : field_inputs) { + input_tooltips_.append(field_input.socket_inspection_name()); + } + + if (log_full_field) { + field_ = std::move(field); + } +} + GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) { static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, @@ -175,13 +210,19 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set<StringRef> names; + geometry_set.attribute_foreach( all_component_types, true, [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &UNUSED(component)) { - if (attribute_id.is_named()) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } }); @@ -376,6 +417,26 @@ void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value geometry_set, log_full_geometry); values_.append({copied_sockets, std::move(value_log)}); } + else if (const FieldCPPType *field_type = dynamic_cast<const FieldCPPType *>(&type)) { + GField field = field_type->get_gfield(value.get()); + bool log_full_field = false; + if (!field.node().depends_on_input()) { + /* Always log constant fields so that their value can be shown in socket inspection. + * In the future we can also evaluate the field here and only store the value. */ + log_full_field = true; + } + if (!log_full_field) { + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_sockets_.contains(socket)) { + log_full_field = true; + break; + } + } + } + destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>( + std::move(field), log_full_field); + values_.append({copied_sockets, std::move(value_log)}); + } else { void *buffer = allocator_->allocate(type.size(), type.alignment()); type.copy_construct(value.get(), buffer); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a3bbca90731..c7a3e795c33 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -36,6 +36,72 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } +void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, + const GeometrySet &geometry_set) const +{ + const int input_index = provider_->dnode->input_by_identifier(identifier).index(); + const SocketDeclaration &decl = *provider_->dnode->declaration()->inputs()[input_index]; + const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl); + if (geo_decl == nullptr) { + return; + } + + const bool only_realized_data = geo_decl->only_realized_data(); + const bool only_instances = geo_decl->only_instances(); + const Span<GeometryComponentType> supported_types = geo_decl->supported_types(); + + if (only_realized_data) { + if (geometry_set.has_instances()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Instances in input geometry are ignored")); + } + } + if (only_instances) { + if (geometry_set.has_realized_data()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Realized data in input geometry is ignored")); + } + } + if (supported_types.is_empty()) { + /* Assume all types are supported. */ + return; + } + const Vector<GeometryComponentType> types_in_geometry = geometry_set.gather_component_types( + true, true); + for (const GeometryComponentType type : types_in_geometry) { + if (type == GEO_COMPONENT_TYPE_INSTANCES) { + continue; + } + if (supported_types.contains(type)) { + continue; + } + std::string message = TIP_("Input geometry has unsupported type: "); + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + message += TIP_("Mesh"); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + message += TIP_("Point Cloud"); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + BLI_assert_unreachable(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + message += TIP_("Volume"); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + message += TIP_("Curve"); + break; + } + } + this->error_message_add(NodeWarningType::Info, std::move(message)); + } +} + const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { for (const InputSocketRef *socket : provider_->dnode->inputs()) { @@ -183,6 +249,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } +std::string GeoNodeExecParams::attribute_producer_name() const +{ + return provider_->dnode->label_or_name() + TIP_(" node"); +} + void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index e823476f9e4..ed5691ebf7f 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "BKE_node.h" @@ -333,6 +334,46 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +Span<GeometryComponentType> Geometry::supported_types() const +{ + return supported_types_; +} + +bool Geometry::only_realized_data() const +{ + return only_realized_data_; +} + +bool Geometry::only_instances() const +{ + return only_instances_; +} + +GeometryBuilder &GeometryBuilder::supported_type(GeometryComponentType supported_type) +{ + decl_->supported_types_ = {supported_type}; + return *this; +} + +GeometryBuilder &GeometryBuilder::supported_type( + blender::Vector<GeometryComponentType> supported_types) +{ + decl_->supported_types_ = std::move(supported_types); + return *this; +} + +GeometryBuilder &GeometryBuilder::only_realized_data(bool value) +{ + decl_->only_realized_data_ = value; + return *this; +} + +GeometryBuilder &GeometryBuilder::only_instances(bool value) +{ + decl_->only_instances_ = value; + return *this; +} + /** \} */ } // namespace blender::nodes::decl diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 2ca797009da..5481465aef6 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -502,11 +502,15 @@ bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const return this->any_output_is_directly_linked(); } -/** - * Sort nodes topologically from left to right or right to left. - * In the future the result if this could be cached on #NodeTreeRef. - */ -Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) const +struct ToposortNodeState { + bool is_done = false; + bool is_in_stack = false; +}; + +static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction, + const NodeRef &start_node, + MutableSpan<ToposortNodeState> node_states, + NodeTreeRef::ToposortResult &result) { struct Item { const NodeRef *node; @@ -516,64 +520,97 @@ Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) int link_index = 0; }; - Vector<const NodeRef *> toposort; - toposort.reserve(nodes_by_id_.size()); - Array<bool> node_is_done_by_id(nodes_by_id_.size(), false); - Stack<Item> nodes_to_check; + /* Do a depth-first search to sort nodes topologically. */ + Stack<Item, 64> nodes_to_check; + nodes_to_check.push({&start_node}); + while (!nodes_to_check.is_empty()) { + Item &item = nodes_to_check.peek(); + const NodeRef &node = *item.node; + const Span<const SocketRef *> sockets = node.sockets( + direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); + + while (true) { + if (item.socket_index == sockets.size()) { + /* All sockets have already been visited. */ + break; + } + const SocketRef &socket = *sockets[item.socket_index]; + const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets(); + if (item.link_index == linked_sockets.size()) { + /* All links connected to this socket have already been visited. */ + item.socket_index++; + item.link_index = 0; + continue; + } + const SocketRef &linked_socket = *linked_sockets[item.link_index]; + const NodeRef &linked_node = linked_socket.node(); + ToposortNodeState &linked_node_state = node_states[linked_node.id()]; + if (linked_node_state.is_done) { + /* The linked node has already been visited. */ + item.link_index++; + continue; + } + if (linked_node_state.is_in_stack) { + result.has_cycle = true; + } + else { + nodes_to_check.push({&linked_node}); + linked_node_state.is_in_stack = true; + } + break; + } - for (const NodeRef *start_node : nodes_by_id_) { - if (node_is_done_by_id[start_node->id()]) { + /* If no other element has been pushed, the current node can be pushed to the sorted list. */ + if (&item == &nodes_to_check.peek()) { + ToposortNodeState &node_state = node_states[node.id()]; + node_state.is_done = true; + node_state.is_in_stack = false; + result.sorted_nodes.append(&node); + nodes_to_check.pop(); + } + } +} + +/** + * Sort nodes topologically from left to right or right to left. + * In the future the result if this could be cached on #NodeTreeRef. + */ +NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const +{ + ToposortResult result; + result.sorted_nodes.reserve(nodes_by_id_.size()); + + Array<ToposortNodeState> node_states(nodes_by_id_.size()); + + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { /* Ignore nodes that are done already. */ continue; } - if (start_node->any_socket_is_directly_linked( + if (node->any_socket_is_directly_linked( direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { /* Ignore non-start nodes. */ continue; } - /* Do a depth-first search to sort nodes topologically. */ - nodes_to_check.push({start_node}); - while (!nodes_to_check.is_empty()) { - Item &item = nodes_to_check.peek(); - const NodeRef &node = *item.node; - const Span<const SocketRef *> sockets = node.sockets( - direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); - - while (true) { - if (item.socket_index == sockets.size()) { - /* All sockets have already been visited. */ - break; - } - const SocketRef &socket = *sockets[item.socket_index]; - const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets(); - if (item.link_index == linked_sockets.size()) { - /* All links connected to this socket have already been visited. */ - item.socket_index++; - item.link_index = 0; - continue; - } - const SocketRef &linked_socket = *linked_sockets[item.link_index]; - const NodeRef &linked_node = linked_socket.node(); - if (node_is_done_by_id[linked_node.id()]) { - /* The linked node has already been visited. */ - item.link_index++; - continue; - } - nodes_to_check.push({&linked_node}); - break; - } + toposort_from_start_node(direction, *node, node_states, result); + } - /* If no other element has been pushed, the current node can be pushed to the sorted list. */ - if (&item == &nodes_to_check.peek()) { - node_is_done_by_id[node.id()] = true; - toposort.append(&node); - nodes_to_check.pop(); + /* Check if the loop above forgot some nodes because there is a cycle. */ + if (result.sorted_nodes.size() < nodes_by_id_.size()) { + result.has_cycle = true; + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { + /* Ignore nodes that are done already. */ + continue; } + /* Start toposort at this node which is somewhere in the middle of a loop. */ + toposort_from_start_node(direction, *node, node_states, result); } } - return toposort; + BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size()); + return result; } const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index e8d4239937f..57a992a4275 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_clamp_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").min(0.0f).max(1.0f).default_value(1.0f); - b.add_input<decl::Float>("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Result"); + b.add_input<decl::Float>(N_("Value")).min(0.0f).max(1.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("Result")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 875e6fa0c35..f8f0ee97eae 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -28,9 +28,9 @@ namespace blender::nodes { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Vector>("Vector").min(-1.0f).max(1.0f); - b.add_output<decl::Vector>("Vector"); + b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Vector")).min(-1.0f).max(1.0f); + b.add_output<decl::Vector>(N_("Vector")); }; } // namespace blender::nodes @@ -175,9 +175,9 @@ namespace blender::nodes { static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Color"); + b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); }; } // namespace blender::nodes @@ -352,9 +352,13 @@ namespace blender::nodes { static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Value").default_value(1.0f); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Float>(N_("Factor")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Value")).default_value(1.0f); + b.add_output<decl::Float>(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index 5ea194ddc83..c866a154e8c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -30,13 +30,13 @@ namespace blender::nodes { static void sh_node_map_range_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("To Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f); - b.add_output<decl::Float>("Result"); + b.add_input<decl::Float>(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("From Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("To Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_output<decl::Float>(N_("Result")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 96d1be49c04..284a5f1189f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -32,10 +32,16 @@ namespace blender::nodes { static void sh_node_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value"), "Value_001") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_input<decl::Float>(N_("Value"), "Value_002") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_output<decl::Float>(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index d4d02e80ada..06fafff578e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::Color>("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Color>("Color"); + b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Color1")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 24c5dcf7ba3..08a9e01786e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output<decl::Float>("R"); - b.add_output<decl::Float>("G"); - b.add_output<decl::Float>("B"); + b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Float>(N_("R")); + b.add_output<decl::Float>(N_("G")); + b.add_output<decl::Float>(N_("B")); }; } // namespace blender::nodes @@ -121,10 +121,10 @@ namespace blender::nodes { static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("R").min(0.0f).max(1.0f); - b.add_input<decl::Float>("G").min(0.0f).max(1.0f); - b.add_input<decl::Float>("B").min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 8ca8fc19521..1bbfa629462 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("X"); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Float>("Z"); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("X")); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Float>(N_("Z")); }; } // namespace blender::nodes @@ -105,10 +105,10 @@ namespace blender::nodes { static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f); - b.add_output<decl::Vector>("Vector"); + b.add_input<decl::Float>(N_("X")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Y")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Z")).min(-10000.0f).max(10000.0f); + b.add_output<decl::Vector>(N_("Vector")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index e90dae60189..b840bd75e42 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -19,39 +19,42 @@ #include "../node_shader_util.h" +#include "BLI_float2.hh" +#include "BLI_float4.hh" + namespace blender::nodes { static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input<decl::Color>("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Color>("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); - b.add_input<decl::Color>("Mortar").default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); - b.add_input<decl::Float>("Scale") + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input<decl::Color>(N_("Mortar")).default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); + b.add_input<decl::Float>(N_("Scale")) .min(-1000.0f) .max(1000.0f) .default_value(5.0f) .no_muted_links(); - b.add_input<decl::Float>("Mortar Size") + b.add_input<decl::Float>(N_("Mortar Size")) .min(0.0f) .max(0.125f) .default_value(0.02f) .no_muted_links(); - b.add_input<decl::Float>("Mortar Smooth").min(0.0f).max(1.0f).no_muted_links(); - b.add_input<decl::Float>("Bias").min(-1.0f).max(1.0f).no_muted_links(); - b.add_input<decl::Float>("Brick Width") + b.add_input<decl::Float>(N_("Mortar Smooth")).min(0.0f).max(1.0f).no_muted_links(); + b.add_input<decl::Float>(N_("Bias")).min(-1.0f).max(1.0f).no_muted_links(); + b.add_input<decl::Float>(N_("Brick Width")) .min(0.01f) .max(100.0f) .default_value(0.5f) .no_muted_links(); - b.add_input<decl::Float>("Row Height") + b.add_input<decl::Float>(N_("Row Height")) .min(0.01f) .max(100.0f) .default_value(0.25f) .no_muted_links(); - b.add_output<decl::Color>("Color"); - b.add_output<decl::Float>("Fac"); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Fac")); }; } // namespace blender::nodes @@ -98,18 +101,185 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat, GPU_constant(&squash_freq)); } -/* node type definition */ +namespace blender::nodes { + +class BrickFunction : public fn::MultiFunction { + private: + const float offset_; + const int offset_freq_; + const float squash_; + const int squash_freq_; + + public: + BrickFunction(const float offset, + const int offset_freq, + const float squash, + const int squash_freq) + : offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"BrickTexture"}; + signature.single_input<float3>("Vector"); + signature.single_input<ColorGeometry4f>("Color1"); + signature.single_input<ColorGeometry4f>("Color2"); + signature.single_input<ColorGeometry4f>("Mortar"); + signature.single_input<float>("Scale"); + signature.single_input<float>("Mortar Size"); + signature.single_input<float>("Mortar Smooth"); + signature.single_input<float>("Bias"); + signature.single_input<float>("Brick Width"); + signature.single_input<float>("Row Height"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + /* Fast integer noise. */ + static float brick_noise(uint n) + { + n = (n + 1013) & 0x7fffffff; + n = (n >> 13) ^ n; + const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return 0.5f * ((float)nn / 1073741824.0f); + } + + static float smoothstepf(const float f) + { + const float ff = f * f; + return (3.0f * ff - 2.0f * ff * f); + } + + static float2 brick(float3 p, + float mortar_size, + float mortar_smooth, + float bias, + float brick_width, + float row_height, + float offset_amount, + int offset_frequency, + float squash_amount, + int squash_frequency) + { + float offset = 0.0f; + + const int rownum = (int)floorf(p.y / row_height); + + if (offset_frequency && squash_frequency) { + brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount; + offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount); + } + + const int bricknum = (int)floorf((p.x + offset) / brick_width); + + const float x = (p.x + offset) - brick_width * bricknum; + const float y = p.y - row_height * rownum; + + const float tint = clamp_f( + brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f); + float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y)); + + float mortar; + if (min_dist >= mortar_size) { + mortar = 0.0f; + } + else if (mortar_smooth == 0.0f) { + mortar = 1.0f; + } + else { + min_dist = 1.0f - min_dist / mortar_size; + mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f; + } + + return float2(tint, mortar); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<ColorGeometry4f> &color1_values = params.readonly_single_input<ColorGeometry4f>( + 1, "Color1"); + const VArray<ColorGeometry4f> &color2_values = params.readonly_single_input<ColorGeometry4f>( + 2, "Color2"); + const VArray<ColorGeometry4f> &mortar_values = params.readonly_single_input<ColorGeometry4f>( + 3, "Mortar"); + const VArray<float> &scale = params.readonly_single_input<float>(4, "Scale"); + const VArray<float> &mortar_size = params.readonly_single_input<float>(5, "Mortar Size"); + const VArray<float> &mortar_smooth = params.readonly_single_input<float>(6, "Mortar Smooth"); + const VArray<float> &bias = params.readonly_single_input<float>(7, "Bias"); + const VArray<float> &brick_width = params.readonly_single_input<float>(8, "Brick Width"); + const VArray<float> &row_height = params.readonly_single_input<float>(9, "Row Height"); + + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(10, "Color"); + MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(11, "Fac"); + + const bool store_fac = !r_fac.is_empty(); + const bool store_color = !r_color.is_empty(); + + for (int64_t i : mask) { + const float2 f2 = brick(vector[i] * scale[i], + mortar_size[i], + mortar_smooth[i], + bias[i], + brick_width[i], + row_height[i], + offset_, + offset_freq_, + squash_, + squash_freq_); + + float4 color_data, color1, color2, mortar; + copy_v4_v4(color_data, color1_values[i]); + copy_v4_v4(color1, color1_values[i]); + copy_v4_v4(color2, color2_values[i]); + copy_v4_v4(mortar, mortar_values[i]); + const float tint = f2.x; + const float f = f2.y; + + if (f != 1.0f) { + const float facm = 1.0f - tint; + color_data = color1 * facm + color2 * tint; + } + + if (store_color) { + color_data = color_data * (1.0f - f) + mortar * f; + copy_v4_v4(r_color[i], color_data); + } + if (store_fac) { + r_fac[i] = f; + } + } + } +}; + +static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexBrick *tex = (NodeTexBrick *)node.storage; + + builder.construct_and_set_matching_fn<BrickFunction>( + tex->offset, tex->offset_freq, tex->squash, tex->squash_freq); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_brick(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_brick_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_brick); node_type_storage( &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_brick); + ntype.build_multi_function = blender::nodes::sh_node_brick_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc index 7ba468a93e0..7c1223a6a32 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -24,16 +24,16 @@ namespace blender::nodes { static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input<decl::Color>("Color1").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Color>("Color2").default_value({0.2f, 0.2f, 0.2f, 1.0f}); - b.add_input<decl::Float>("Scale") + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input<decl::Float>(N_("Scale")) .min(-10000.0f) .max(10000.0f) .default_value(5.0f) .no_muted_links(); - b.add_output<decl::Color>("Color"); - b.add_output<decl::Float>("Fac"); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Fac")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index 4796af02361..33832c42b3c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").hide_value().implicit_field(); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Float>("Fac").no_muted_links(); + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index df1051c07b4..f20fc85cbe0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -24,9 +24,9 @@ namespace blender::nodes { static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").implicit_field(); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Float>("Alpha").no_muted_links(); + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Alpha")).no_muted_links(); }; }; // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index b6cdcf86528..62e68d53d03 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -24,11 +24,11 @@ namespace blender::nodes { static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").implicit_field(); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(1.0f); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Float>("Fac").no_muted_links(); + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 61c26d07e2f..3bf4e24ed53 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -26,15 +26,15 @@ namespace blender::nodes { static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").hide_value().implicit_field(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input<decl::Float>("Dimension").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input<decl::Float>("Lacunarity").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input<decl::Float>("Offset").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Gain").min(0.0f).max(1000.0f).default_value(1.0f); - b.add_output<decl::Float>("Fac").no_muted_links(); + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Offset")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 6ffc8979815..72892c32795 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").implicit_field(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input<decl::Float>("Roughness") + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Roughness")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_output<decl::Float>("Fac").no_muted_links(); - b.add_output<decl::Color>("Color").no_muted_links(); + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 574260f3c36..422268b98c3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -26,25 +26,25 @@ namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").hide_value().implicit_field(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Smoothness") + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Smoothness")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Exponent").min(0.0f).max(32.0f).default_value(0.5f); - b.add_input<decl::Float>("Randomness") + b.add_input<decl::Float>(N_("Exponent")).min(0.0f).max(32.0f).default_value(0.5f); + b.add_input<decl::Float>(N_("Randomness")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR); - b.add_output<decl::Float>("Distance").no_muted_links(); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Vector>("Position").no_muted_links(); - b.add_output<decl::Float>("W").no_muted_links(); - b.add_output<decl::Float>("Radius").no_muted_links(); + b.add_output<decl::Float>(N_("Distance")).no_muted_links(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Vector>(N_("Position")).no_muted_links(); + b.add_output<decl::Float>(N_("W")).no_muted_links(); + b.add_output<decl::Float>(N_("Radius")).no_muted_links(); }; } // namespace blender::nodes @@ -239,16 +239,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { return params.readonly_single_input<float>(param_index, "Randomness"); }; auto get_r_distance = [&](int param_index) -> MutableSpan<float> { - return params.uninitialized_single_output<float>(param_index, "Distance"); + return params.uninitialized_single_output_if_required<float>(param_index, "Distance"); }; auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> { - return params.uninitialized_single_output<ColorGeometry4f>(param_index, "Color"); + return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color"); }; auto get_r_position = [&](int param_index) -> MutableSpan<float3> { - return params.uninitialized_single_output<float3>(param_index, "Position"); + return params.uninitialized_single_output_if_required<float3>(param_index, "Position"); }; auto get_r_w = [&](int param_index) -> MutableSpan<float> { - return params.uninitialized_single_output<float>(param_index, "W"); + return params.uninitialized_single_output_if_required<float>(param_index, "W"); }; int param = 0; @@ -263,6 +263,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -271,12 +274,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -288,6 +295,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -296,12 +306,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -314,6 +328,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -324,12 +341,16 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -346,6 +367,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -353,11 +377,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -369,6 +397,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -376,11 +407,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -393,6 +428,9 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -402,11 +440,15 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { exponent[i], rand, SHD_VORONOI_MINKOWSKI, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -425,16 +467,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f1(p, exponent[i], rand, SHD_VORONOI_F1, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f1(p, + exponent[i], + rand, + SHD_VORONOI_F1, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -448,17 +508,34 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f2( - p, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f2(p, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -473,18 +550,36 @@ class VoronoiMinowskiFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_smooth_f1( - p, smth, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_smooth_f1(p, + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -572,16 +667,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { return params.readonly_single_input<float>(param_index, "Randomness"); }; auto get_r_distance = [&](int param_index) -> MutableSpan<float> { - return params.uninitialized_single_output<float>(param_index, "Distance"); + return params.uninitialized_single_output_if_required<float>(param_index, "Distance"); }; auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> { - return params.uninitialized_single_output<ColorGeometry4f>(param_index, "Color"); + return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color"); }; auto get_r_position = [&](int param_index) -> MutableSpan<float3> { - return params.uninitialized_single_output<float3>(param_index, "Position"); + return params.uninitialized_single_output_if_required<float3>(param_index, "Position"); }; auto get_r_w = [&](int param_index) -> MutableSpan<float> { - return params.uninitialized_single_output<float>(param_index, "W"); + return params.uninitialized_single_output_if_required<float>(param_index, "W"); }; int param = 0; @@ -595,13 +690,24 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f1(p, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_f1(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -612,13 +718,24 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f2(p, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_f2(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -630,14 +747,26 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float p = w[i] * scale[i]; const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_smooth_f1(p, smth, rand, &r_distance[i], &col, &r_w[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_w[i] = safe_divide(r_w[i], scale[i]); + noise::voronoi_smooth_f1(p, + smth, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } } break; } @@ -653,6 +782,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -661,12 +793,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -677,6 +813,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; @@ -685,12 +824,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -702,6 +845,9 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); @@ -712,12 +858,16 @@ class VoronoiMetricFunction : public fn::MultiFunction { 0.0f, rand, metric_, - &r_distance[i], - &col, - &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float2::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, 0.0f); + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } } break; } @@ -733,13 +883,25 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f1( - vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + noise::voronoi_f1(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -750,13 +912,25 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); float3 col; - noise::voronoi_f2( - vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + noise::voronoi_f2(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } } break; } @@ -768,21 +942,31 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<float> r_distance = get_r_distance(param++); MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); - for (int64_t i : mask) { - const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); - const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); - float3 col; - noise::voronoi_smooth_f1(vector[i] * scale[i], - smth, - 0.0f, - rand, - metric_, - &r_distance[i], - &col, - &r_position[i]); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - r_position[i] = float3::safe_divide(r_position[i], scale[i]); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + { + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } } + break; } } @@ -799,16 +983,34 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f1(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f1(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -821,16 +1023,34 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_f2(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_f2(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } @@ -844,17 +1064,36 @@ class VoronoiMetricFunction : public fn::MultiFunction { MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); MutableSpan<float3> r_position = get_r_position(param++); MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); for (int64_t i : mask) { const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; float3 col; float4 pos; - noise::voronoi_smooth_f1(p, smth, 0.0f, rand, metric_, &r_distance[i], &col, &pos); - r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); - pos = float4::safe_divide(pos, scale[i]); - r_position[i] = float3(pos.x, pos.y, pos.z); - r_w[i] = pos.w; + noise::voronoi_smooth_f1(p, + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } } break; } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index 25e65e3d3f0..144aa1885bd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -26,19 +26,19 @@ namespace blender::nodes { static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").implicit_field(); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input<decl::Float>("Detail Scale").min(-1000.0f).max(1000.0f).default_value(1.0f); - b.add_input<decl::Float>("Detail Roughness") + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(16.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("Detail Roughness")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Phase Offset").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Float>("Fac").no_muted_links(); + b.add_input<decl::Float>(N_("Phase Offset")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 445b201e419..43ee9400551 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -26,10 +26,10 @@ namespace blender::nodes { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input<decl::Float>("W").min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Value"); - b.add_output<decl::Color>("Color"); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("Value")); + b.add_output<decl::Color>(N_("Color")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 2544ea1921c..e4f1b2c76f0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -34,9 +34,9 @@ namespace blender::nodes { static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>("Color"); - b.add_output<decl::Float>("Alpha"); + b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Alpha")); }; } // namespace blender::nodes @@ -192,8 +192,8 @@ namespace blender::nodes { static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Color").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Float>("Val"); + b.add_input<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Float>(N_("Val")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 1344ce5c5d9..b0f152d8526 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void sh_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Value"); + b.add_output<decl::Float>(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index f49ff06cef1..ca5aeea9a7d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -30,12 +30,12 @@ namespace blender::nodes { static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); - b.add_input<decl::Vector>("Vector", "Vector_001").min(-10000.0f).max(10000.0f); - b.add_input<decl::Vector>("Vector", "Vector_002").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Scale").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Vector>("Vector"); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(-10000.0f).max(10000.0f); + b.add_input<decl::Vector>(N_("Vector"), "Vector_002").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Vector>(N_("Vector")); + b.add_output<decl::Float>(N_("Value")); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index c9b26fa5199..1ab643bc3fa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value(); - b.add_input<decl::Vector>("Center"); - b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER); - b.add_output<decl::Vector>("Vector"); + b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::Vector>(N_("Center")); + b.add_input<decl::Vector>(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); + b.add_output<decl::Vector>(N_("Vector")); }; } // namespace blender::nodes diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index 5e938db0c35..2211cc931da 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -99,21 +99,21 @@ static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args) return PyFloat_FromDouble(a * (1.0 - x) + b * x); } -PyDoc_STRVAR( - py_bl_math_smoothstep_doc, - ".. function:: smoothstep(from_value, to_value, value)\n" - "\n" - " Performs smooth interpolation between 0 and 1 as value changes between from and to values.\n" - " Outside the range the function returns the same value as the nearest edge.\n" - "\n" - " :arg from_value: The edge value where the result is 0.\n" - " :type from_value: float\n" - " :arg to_value: The edge value where the result is 1.\n" - " :type to_value: float\n" - " :arg factor: The interpolation value.\n" - " :type factor: float\n" - " :return: The interpolated value in [0.0, 1.0].\n" - " :rtype: float\n"); +PyDoc_STRVAR(py_bl_math_smoothstep_doc, + ".. function:: smoothstep(from_value, to_value, value)\n" + "\n" + " Performs smooth interpolation between 0 and 1 as value changes between from and " + "to values.\n" + " Outside the range the function returns the same value as the nearest edge.\n" + "\n" + " :arg from_value: The edge value where the result is 0.\n" + " :type from_value: float\n" + " :arg to_value: The edge value where the result is 1.\n" + " :type to_value: float\n" + " :arg factor: The interpolation value.\n" + " :type factor: float\n" + " :return: The interpolated value in [0.0, 1.0].\n" + " :rtype: float\n"); static PyObject *py_bl_math_smoothstep(PyObject *UNUSED(self), PyObject *args) { double a, b, x; diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index d2a47a13db3..4de7c09640b 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -104,6 +104,11 @@ void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, SeqCollection *collection); void SEQ_filter_selected_strips(SeqCollection *collection); +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index c9024614dfd..df3c9a40409 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -32,6 +32,8 @@ struct Scene; struct Sequence; struct rctf; +void SEQ_timeline_init_boundbox(const struct Scene *scene, struct rctf *rect); +void SEQ_timeline_expand_boundbox(const struct ListBase *seqbase, struct rctf *rect); void SEQ_timeline_boundbox(const struct Scene *scene, const struct ListBase *seqbase, struct rctf *rect); diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 427a8835879..b9ee23a9186 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2451,7 +2451,6 @@ static void do_transform_effect(const SeqRenderData *context, int total_lines, ImBuf *out) { - Scene *scene = context->scene; TransformVars *transform = (TransformVars *)seq->effectdata; float scale_x, scale_y, translate_x, translate_y, rotate_radians; @@ -2469,10 +2468,14 @@ static void do_transform_effect(const SeqRenderData *context, /* Translate */ if (!transform->percent) { - float rd_s = (scene->r.size / 100.0f); + /* Compensate text size for preview render size. */ + double proxy_size_comp = context->scene->r.size / 100.0; + if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) { + proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size); + } - translate_x = transform->xIni * rd_s + (x / 2.0f); - translate_y = transform->yIni * rd_s + (y / 2.0f); + translate_x = transform->xIni * proxy_size_comp + (x / 2.0f); + translate_y = transform->yIni * proxy_size_comp + (y / 2.0f); } else { translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f); diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index a12a5cbdc61..68f632ddb28 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -520,3 +520,29 @@ void SEQ_filter_selected_strips(SeqCollection *collection) } } } + +static void seq_collection_to_tag(ListBase *seqbase, SeqCollection *collection) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + seq->tmp_tag = false; + } + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq->tmp_tag = true; + } +} + +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = SEQ_query_rendered_strips( + seqbase, timeline_frame, displayed_channel); + + seq_collection_to_tag(seqbase, collection); + + const int len = SEQ_collection_len(collection); + SEQ_collection_free(collection); + return len; +} diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 1c5f4c3ab76..92ac580f3b1 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -376,19 +376,27 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq) } /** - * Define boundary rectangle of sequencer timeline and fill in rect data + * Initialize given rectangle with the Scene's timeline boundaries. * - * \param scene: Scene in which strips are located - * \param seqbase: ListBase in which strips are located - * \param rect: data structure describing rectangle, that will be filled in by this function + * \param scene: the Scene instance whose timeline boundaries are extracted from + * \param rect: output parameter to be filled with timeline boundaries */ -void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +void SEQ_timeline_init_boundbox(const Scene *scene, rctf *rect) { rect->xmin = scene->r.sfra; rect->xmax = scene->r.efra + 1; rect->ymin = 0.0f; rect->ymax = 8.0f; +} +/** + * Stretch the given rectangle to include the given strips boundaries + * + * \param seqbase: ListBase in which strips are located + * \param rect: output parameter to be filled with strips' boundaries + */ +void SEQ_timeline_expand_boundbox(const ListBase *seqbase, rctf *rect) +{ if (seqbase == NULL) { return; } @@ -406,6 +414,19 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re } } +/** + * Define boundary rectangle of sequencer timeline and fill in rect data + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param rect: data structure describing rectangle, that will be filled in by this function + */ +void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +{ + SEQ_timeline_init_boundbox(scene, rect); + SEQ_timeline_expand_boundbox(seqbase, rect); +} + static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame) { Sequence *seq; diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4d65726fe2b..03b2fb49085 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -202,6 +202,7 @@ if(WITH_XR_OPENXR) xr/intern/wm_xr_action.c xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c + xr/intern/wm_xr_operators.c xr/intern/wm_xr_session.c xr/wm_xr.h diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index eaf32c06aba..112d76a3e65 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -276,10 +276,10 @@ typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, struct wmEventHandler_Keymap *handler, struct wmEventHandler_KeymapResult *km_result); -void WM_event_get_keymap_from_toolsystem_fallback(struct wmWindowManager *wm, - struct wmWindow *win, - struct wmEventHandler_Keymap *handler, - wmEventHandler_KeymapResult *km_result); +void WM_event_get_keymap_from_toolsystem_with_gizmos(struct wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, struct wmWindow *win, struct wmEventHandler_Keymap *handler, @@ -740,24 +740,38 @@ struct wmDropBox *WM_dropbox_add( void (*copy)(struct wmDrag *, struct wmDropBox *), void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), WMDropboxTooltipFunc tooltip); +void WM_drag_draw_item_name_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); +void WM_drag_draw_default_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra); +bool WM_drag_asset_will_import_linked(const wmDrag *drag); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, + struct AssetMetaData *metadata, const char *path, int import_type); struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); +struct AssetMetaData *WM_drag_get_asset_meta_data(const struct wmDrag *drag, int idcode); struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag); + void WM_drag_add_asset_list_item(wmDrag *drag, const struct bContext *C, const struct AssetLibraryReference *asset_library_ref, @@ -923,6 +937,7 @@ bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); +bool WM_cursor_test_motion_and_update(const int mval[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; int WM_event_drag_threshold(const struct wmEvent *event); bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); @@ -1017,6 +1032,13 @@ bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]); +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]); +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]); +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]); +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale); +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale); +void WM_xr_session_state_navigation_reset(struct wmXrSessionState *state); struct ARegionType *WM_xr_surface_controller_region_type_get(void); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index f4595869baf..9f427a90353 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -119,6 +119,7 @@ struct wmWindowManager; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" +#include "DNA_uuid_types.h" #include "DNA_vec_types.h" #include "DNA_xr_types.h" #include "RNA_types.h" @@ -967,6 +968,7 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata #define WM_DRAG_VALUE 6 #define WM_DRAG_COLOR 7 #define WM_DRAG_DATASTACK 8 +#define WM_DRAG_ASSET_CATALOG 9 typedef enum wmDragFlags { WM_DRAG_NOP = 0, @@ -989,6 +991,7 @@ typedef struct wmDragAsset { /* Always freed. */ const char *path; int id_type; + struct AssetMetaData *metadata; int import_type; /* eFileAssetImportType */ /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the @@ -999,6 +1002,10 @@ typedef struct wmDragAsset { struct bContext *evil_C; } wmDragAsset; +typedef struct wmDragAssetCatalog { + bUUID drag_catalog_id; +} wmDragAssetCatalog; + /** * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really @@ -1020,7 +1027,7 @@ typedef struct wmDragAssetListItem { typedef char *(*WMDropboxTooltipFunc)(struct bContext *, struct wmDrag *, - const struct wmEvent *event, + const int xy[2], struct wmDropBox *drop); typedef struct wmDrag { @@ -1038,8 +1045,16 @@ typedef struct wmDrag { float scale; int sx, sy; - /** If filled, draws operator tooltip/operator name. */ - char tooltip[200]; + /** Informs which dropbox is activated with the drag item. + * When this value changes, the #draw_activate and #draw_deactivate dropbox callbacks are + * triggered. + */ + struct wmDropBox *active_dropbox; + /* Text to show when the operator poll fails. Typically the message the + * operator set with CTX_wm_operator_poll_msg_set(). */ + const char *disabled_info; + bool free_disabled_info; + unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ @@ -1067,6 +1082,18 @@ typedef struct wmDropBox { */ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + /** Override the default drawing function. */ + void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *); + + /** Called when pool returns true the first time. */ + void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag); + + /** Called when pool returns false the first time or when the drag event ends. */ + void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag); + + /** Custom data for drawing. */ + void *draw_data; + /** Custom tooltip shown during dragging. */ WMDropboxTooltipFunc tooltip; diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 0b7d5e5f1f4..47ee296823b 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -86,18 +86,23 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) wmWindowManager *wm = (wmWindowManager *)id; LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - BKE_LIB_FOREACHID_PROCESS(data, win->scene, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->scene, IDWALK_CB_USER_ONE); /* 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 9af90355a79..b9f0e09d106 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -40,9 +40,13 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_screen.h" + +#include "GHOST_C-api.h" #include "BLO_readfile.h" @@ -63,6 +67,7 @@ #include "WM_api.h" #include "WM_types.h" #include "wm_event_system.h" +#include "wm_window.h" /* ****************************************************** */ @@ -176,6 +181,7 @@ wmDrag *WM_event_start_drag( } break; case WM_DRAG_ASSET: + case WM_DRAG_ASSET_CATALOG: /* Move ownership of poin to wmDrag. */ drag->poin = poin; drag->flags |= WM_DRAG_FREE_DATA; @@ -202,6 +208,31 @@ wmDrag *WM_event_start_drag( return drag; } +/** + * Additional work to cleanly end dragging. Additional because this doesn't actually remove the + * drag items. Should be called whenever dragging is stopped (successful or not, also when + * canceled). + */ +void wm_drags_exit(wmWindowManager *wm, wmWindow *win) +{ + bool any_active = false; + LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) { + if (drag->active_dropbox) { + any_active = true; + break; + } + } + + /* If there is no active drop-box #wm_drags_check_ops() set a stop-cursor, which needs to be + * restored. */ + if (!any_active) { + WM_cursor_modal_restore(win); + /* Ensure the correct area cursor is restored. */ + win->tag_cursor_refresh = true; + WM_event_add_mousemove(win); + } +} + void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) { drag->imb = imb; @@ -229,9 +260,15 @@ void WM_drag_data_free(int dragtype, void *poin) void WM_drag_free(wmDrag *drag) { + if (drag->active_dropbox && drag->active_dropbox->draw_deactivate) { + drag->active_dropbox->draw_deactivate(drag->active_dropbox, drag); + } if (drag->flags & WM_DRAG_FREE_DATA) { WM_drag_data_free(drag->type, drag->poin); } + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } BLI_freelistN(&drag->ids); LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) { if (asset_item->is_external) { @@ -250,11 +287,11 @@ void WM_drag_free_list(struct ListBase *lb) } } -static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) +static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop) { char *tooltip = NULL; if (drop->tooltip) { - tooltip = drop->tooltip(C, drag, event, drop); + tooltip = drop->tooltip(C, drag, xy, drop); } if (!tooltip) { tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); @@ -269,15 +306,35 @@ static wmDropBox *dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + drag->disabled_info = NULL; + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { - if (drop->poll(C, drag, event) && - WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + if (!drop->poll(C, drag, event)) { + /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. + * Instead show it only if the drop box could be used in principle, but the operator + * can't be executed. */ + continue; + } + + if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) { return drop; } + + /* Attempt to set the disabled hint when the poll fails. Will always be the last hint set + * when there are multiple failing polls (could allow multiple disabled-hints too). */ + bool free_disabled_info = false; + const char *disabled_hint = CTX_wm_operator_poll_msg_get(C, &free_disabled_info); + if (disabled_hint) { + drag->disabled_info = disabled_hint; + drag->free_disabled_info = free_disabled_info; + } } } } @@ -286,7 +343,7 @@ static wmDropBox *dropbox_active(bContext *C, } /* return active operator tooltip/name when mouse is in box */ -static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) +static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); @@ -298,13 +355,13 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); drop = dropbox_active(C, ®ion->handlers, drag, event); } - if (drop) { - return dropbox_tooltip(C, drag, event, drop); - } - return NULL; + return drop; } -static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) +/** + * Update dropping information for the current mouse position in \a event. + */ +static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const int winsize_x = WM_window_pixels_x(win); @@ -316,24 +373,30 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e return; } - drag->tooltip[0] = 0; - - /* check buttons (XXX todo rna and value) */ - if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); + wmDropBox *drop_prev = drag->active_dropbox; + wmDropBox *drop = wm_dropbox_active(C, drag, event); + if (drop != drop_prev) { + if (drop_prev && drop_prev->draw_deactivate) { + drop_prev->draw_deactivate(drop_prev, drag); + BLI_assert(drop_prev->draw_data == NULL); + } + if (drop && drop->draw_activate) { + drop->draw_activate(drop, drag); + } + drag->active_dropbox = drop; } - else { - char *tooltip = wm_dropbox_active(C, drag, event); +} - if (tooltip) { - BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); - MEM_freeN(tooltip); - // WM_cursor_modal_set(win, WM_CURSOR_COPY); - } - // else - // WM_cursor_modal_restore(win); - /* unsure about cursor type, feels to be too much */ +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *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)) { + drop->copy(drag, drop); } + + wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C)); } /* called in inner handler loop, region context */ @@ -341,8 +404,19 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); + bool any_active = false; LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - wm_drop_operator_options(C, drag, event); + wm_drop_update_active(C, drag, event); + + if (drag->active_dropbox) { + any_active = true; + } + } + + /* Change the cursor to display that dropping isn't possible here. But only if there is something + * being dragged actually. Cursor will be restored in #wm_drags_exit(). */ + if (!BLI_listbase_is_empty(&wm->drags)) { + WM_cursor_modal_set(CTX_wm_window(C), any_active ? WM_CURSOR_DEFAULT : WM_CURSOR_STOP); } } @@ -407,11 +481,15 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ -wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type) +wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, + AssetMetaData *metadata, + const char *path, + int import_type) { wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); + asset_drag->metadata = metadata; asset_drag->path = path; asset_drag->id_type = ED_asset_handle_get_id_type(asset); asset_drag->import_type = import_type; @@ -435,8 +513,30 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; } -static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) +{ + wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode); + if (drag_asset) { + return drag_asset->metadata; + } + + ID *local_id = WM_drag_get_local_ID(drag, idcode); + if (local_id) { + return local_id->asset_data; + } + + return NULL; +} + +/** + * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag). + */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) { + /* Only support passing in limited flags. */ + BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); + eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; @@ -450,14 +550,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(bmain, - scene, - view_layer, - view3d, - asset_drag->path, - idtype, - name, - FILE_ACTIVE_COLLECTION); + return WM_file_link_datablock( + bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag); case FILE_ASSET_IMPORT_APPEND: return WM_file_append_datablock(bmain, scene, @@ -466,7 +560,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); case FILE_ASSET_IMPORT_APPEND_REUSE: return WM_file_append_datablock(G_MAIN, @@ -476,7 +570,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } @@ -485,12 +579,24 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) return NULL; } +bool WM_drag_asset_will_import_linked(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET) { + return false; + } + + const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + return asset_drag->import_type == FILE_ASSET_IMPORT_LINK; +} + /** * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append * that depending on what was chosen by the drag-box (currently append only in fact). * * Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset * import is rolled back if the drop operator fails. + * + * \param flag: #eFileSel_Params_Flag passed to linking code. */ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { @@ -508,7 +614,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /* Link/append the asset. */ - return wm_drag_asset_id_import(asset_drag); + return WM_drag_asset_id_import(asset_drag, 0); } /** @@ -544,6 +650,15 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } } +wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_CATALOG) { + return NULL; + } + + return drag->poin; +} + /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ @@ -568,11 +683,12 @@ void WM_drag_add_asset_list_item( drag_asset->asset_data.local_id = local_id; } else { + AssetMetaData *metadata = ED_asset_handle_get_metadata(asset); char asset_blend_path[FILE_MAX_LIBEXTRA]; ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path); drag_asset->is_external = true; drag_asset->asset_data.external_info = WM_drag_create_asset_data( - asset, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); + asset, metadata, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); } BLI_addtail(&drag->asset_items, drag_asset); } @@ -603,6 +719,17 @@ static void wm_drop_operator_draw(const char *name, int x, int y) UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } +static void wm_drop_redalert_draw(const char *redalert_str, int x, int y) +{ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float col_fg[4]; + + UI_GetThemeColor4fv(TH_REDALERT, col_fg); + + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, redalert_str, col_fg, col_bg); +} + const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { @@ -629,132 +756,172 @@ const char *WM_drag_get_item_name(wmDrag *drag) return ""; } -static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2) +static void wm_drag_draw_icon(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) { - if (rect->xmin > x1) { - rect->xmin = x1; - } - if (rect->xmax < x2) { - rect->xmax = x2; + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + y = xy[1] - drag->sy / 2; + + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, + x, + y, + drag->imb->x, + drag->imb->y, + GPU_RGBA8, + false, + drag->imb->rect, + drag->scale, + drag->scale, + 1.0f, + 1.0f, + col); } - if (rect->ymin > y1) { - rect->ymin = y1; - } - if (rect->ymax < y2) { - rect->ymax = y2; + else { + int padding = 4 * UI_DPI_FAC; + x = xy[0] - 2 * padding; + y = xy[1] - 2 * UI_DPI_FAC; + + const uchar text_col[] = {255, 255, 255, 255}; + UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); } } -/* called in wm_draw.c */ -/* if rect set, do not draw */ -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) +static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - wmWindowManager *wm = CTX_wm_manager(C); - const int winsize_y = WM_window_pixels_y(win); + const uchar text_col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); +} - int cursorx = win->eventstate->xy[0]; - int cursory = win->eventstate->xy[1]; - if (rect) { - rect->xmin = rect->xmax = cursorx; - rect->ymin = rect->ymax = cursory; - } +void WM_drag_draw_item_name_fn(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) +{ + int x = xy[0] + 10 * UI_DPI_FAC; + int y = xy[1] + 1 * UI_DPI_FAC; - /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */ - GPU_blend(GPU_BLEND_ALPHA); - LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - const uchar text_col[] = {255, 255, 255, 255}; - int iconsize = UI_DPI_ICON_SIZE; - int padding = 4 * UI_DPI_FAC; + wm_drag_draw_item_name(drag, x, y); +} - /* image or icon */ - int x, y; - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2; +static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + if (!CTX_wm_region(C)) { + /* Some callbacks require the region. */ + return; + } + int iconsize = UI_DPI_ICON_SIZE; + int padding = 4 * UI_DPI_FAC; - if (rect) { - drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy); - } - else { - float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTexScaled(&state, - x, - y, - drag->imb->x, - drag->imb->y, - GPU_RGBA8, - false, - drag->imb->rect, - drag->scale, - drag->scale, - 1.0f, - 1.0f, - col); - } - } - else { - x = cursorx - 2 * padding; - y = cursory - 2 * UI_DPI_FAC; + char *tooltip = NULL; + if (drag->active_dropbox) { + tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); + } - if (rect) { - drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize); - } - else { - UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); - } - } + if (!tooltip && !drag->disabled_info) { + return; + } + + const int winsize_y = WM_window_pixels_y(win); + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; - /* item name */ - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2 - iconsize; + if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { + y = xy[1] + drag->sy / 2 + padding; } else { - x = cursorx + 10 * UI_DPI_FAC; - y = cursory + 1 * UI_DPI_FAC; + y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; } + } + else { + x = xy[0] - 2 * padding; - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); + if (xy[1] + iconsize + iconsize < winsize_y) { + y = (xy[1] + iconsize) + padding; } else { - UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); + y = (xy[1] - iconsize) - padding; } + } - /* operator name with roundbox */ - if (drag->tooltip[0]) { - if (drag->imb) { - x = cursorx - drag->sx / 2; + if (tooltip) { + wm_drop_operator_draw(tooltip, x, y); + MEM_freeN(tooltip); + } + else if (drag->disabled_info) { + wm_drop_redalert_draw(drag->disabled_info, x, y); + } +} - if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) { - y = cursory + drag->sy / 2 + padding; - } - else { - y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize; - } - } - else { - x = cursorx - 2 * padding; +static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + int xy_tmp[2] = {UNPACK2(xy)}; - if (cursory + iconsize + iconsize < winsize_y) { - y = (cursory + iconsize) + padding; - } - else { - y = (cursory - iconsize) - padding; - } - } + /* Image or icon. */ + wm_drag_draw_icon(C, win, drag, xy_tmp); - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); - } - else { - wm_drop_operator_draw(drag->tooltip, x, y); - } + /* Item name. */ + if (drag->imb) { + int iconsize = UI_DPI_ICON_SIZE; + xy_tmp[0] = xy[0] - (drag->sx / 2); + xy_tmp[1] = xy[1] - (drag->sy / 2) - iconsize; + } + else { + xy_tmp[0] = xy[0] + 10 * UI_DPI_FAC; + xy_tmp[1] = xy[1] + 1 * UI_DPI_FAC; + } + wm_drag_draw_item_name(drag, UNPACK2(xy_tmp)); + + /* Operator name with roundbox. */ + wm_drag_draw_tooltip(C, win, drag, xy); +} + +void WM_drag_draw_default_fn(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + wm_drag_draw_default(C, win, drag, xy); +} + +/* Called in #wm_draw_window_onscreen. */ +void wm_drags_draw(bContext *C, wmWindow *win) +{ + int xy[2]; + if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { + wm_cursor_position_get(win, &xy[0], &xy[1]); + } + else { + xy[0] = win->eventstate->xy[0]; + xy[1] = win->eventstate->xy[1]; + } + + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy)); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy)); + if (region) { + BLI_assert(!CTX_wm_area(C) && !CTX_wm_region(C)); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + } + + wmWindowManager *wm = CTX_wm_manager(C); + + /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */ + GPU_blend(GPU_BLEND_ALPHA); + LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { + if (drag->active_dropbox && drag->active_dropbox->draw) { + drag->active_dropbox->draw(C, win, drag, xy); + continue; } + + wm_drag_draw_default(C, win, drag, xy); } GPU_blend(GPU_BLEND_NONE); + CTX_wm_area_set(C, NULL); + CTX_wm_region_set(C, NULL); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index b5e81528e2b..8acce240046 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -856,9 +856,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) wm_gesture_draw(win); } - /* needs pixel coords in screen */ + /* Needs pixel coords in screen. */ if (wm->drags.first) { - wm_drags_draw(C, win, NULL); + wm_drags_draw(C, win); } GPU_debug_group_end(); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 9ee114674ed..ef733837bf7 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -276,6 +276,28 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } +/** + * Detect motion between selection (callers should only use this for selection picking), + * typically mouse press/click events. + * + * \param mval: Region relative coordinates, call with (-1, -1) resets the last cursor location. + * \returns True when there was motion since last called. + * + * NOTE(@campbellbarton): The logic used here isn't foolproof. + * It's possible that users move the cursor past #WM_EVENT_CURSOR_MOTION_THRESHOLD then back to + * a position within the threshold (between mouse clicks). + * In practice users never reported this since the threshold is very small (a few pixels). + * To prevent the unlikely case of values matching from another region, + * changing regions resets this value to (-1, -1). + */ +bool WM_cursor_test_motion_and_update(const int mval[2]) +{ + static int mval_prev[2] = {-1, -1}; + bool use_cycle = (len_manhattan_v2v2_int(mval, mval_prev) <= WM_EVENT_CURSOR_MOTION_THRESHOLD); + copy_v2_v2_int(mval_prev, mval); + return !use_cycle; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index df976d9a4cd..798f60fba3d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3055,12 +3055,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis ListBase *lb = (ListBase *)event->customdata; LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) { if (drop->poll(C, drag, event)) { - /* 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)) { - drop->copy(drag, drop); - } + wm_drop_prepare(C, drag, drop); /* Pass single matched wmDrag onto the operator. */ BLI_remlink(lb, drag); @@ -3094,6 +3089,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis break; } } + /* Always exit all drags on a drop event, even if poll didn't succeed. */ + wm_drags_exit(wm, win); } } } @@ -3390,6 +3387,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv screen->do_draw_drag = true; } else if (event->type == EVT_ESCKEY) { + wm_drags_exit(wm, win); WM_drag_free_list(&wm->drags); screen->do_draw_drag = true; @@ -3630,7 +3628,8 @@ void wm_event_do_handlers(bContext *C) /* Clear tool-tip on mouse move. */ if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > U.move_threshold) { + if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > + WM_EVENT_CURSOR_MOTION_THRESHOLD) { WM_tooltip_clear(C, win); } } @@ -4010,10 +4009,12 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * * Follow #wmEventHandler_KeymapDynamicFn signature. */ -void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, - wmWindow *win, - wmEventHandler_Keymap *handler, - wmEventHandler_KeymapResult *km_result) +static void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result, + /* Extra arguments. */ + const bool with_gizmos) { memset(km_result, 0x0, sizeof(*km_result)); @@ -4046,7 +4047,8 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { add_keymap = true; } - if (tref_rt->gizmo_group[0] != '\0') { + + if (with_gizmos && (tref_rt->gizmo_group[0] != '\0')) { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -4070,6 +4072,7 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, } } } + if (add_keymap) { keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback; } @@ -4097,32 +4100,20 @@ void WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, } } +void WM_event_get_keymap_from_toolsystem_with_gizmos(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) +{ + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, true); +} + void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, - wmWindow *UNUSED(win), + wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) { - memset(km_result, 0x0, sizeof(*km_result)); - - ScrArea *area = handler->dynamic.user_data; - handler->keymap_tool = NULL; - bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap[0]) { - const char *keymap_id = tref_rt->keymap; - { - wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( - &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); - /* We shouldn't use keymaps from unrelated spaces. */ - if (km != NULL) { - handler->keymap_tool = area->runtime.tool; - km_result->keymaps[km_result->keymaps_len++] = km; - } - else { - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); - } - } - } + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, false); } struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index e203281297b..67222cc07f9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1644,6 +1644,7 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C, area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0); if (area) { v3d = area->spacedata.first; + region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); } } @@ -1787,6 +1788,7 @@ static bool wm_file_write(bContext *C, /* Call pre-save callbacks before writing preview, * that way you can generate custom file thumbnail. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* Enforce full override check/generation on file save. */ BKE_lib_override_library_main_operations_create(bmain, true); @@ -2104,6 +2106,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* check current window and close it if temp */ if (win && WM_window_is_temp_screen(win)) { @@ -2139,7 +2142,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } printf("ok\n"); - + BKE_report(op->reports, RPT_INFO, "Startup file saved"); G.save_over = 0; BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index c88e577df6a..7d74ac9605b 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -583,8 +583,13 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items, * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be * processed, so we need to recursively deal with them here. */ - BKE_library_foreach_ID_link( - cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); + /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it + * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of + * shapekey referencing the shapekey itself). */ + if (id != cb_data->id_self) { + BKE_library_foreach_ID_link( + cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); + } return IDWALK_RET_NOP; } @@ -807,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); } /* Remove linked IDs when a local existing data has been reused instead. */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index d0693d37ef4..c382af03c4a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -548,7 +548,6 @@ void WM_exit_ex(bContext *C, const bool do_python) ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */ ED_assetlist_storage_exit(); - ED_view3d_cursor_snap_exit(); if (wm) { /* Before BKE_blender_free! - since the ListBases get freed there. */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 89f0206d72e..1130ad9a558 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3759,87 +3759,6 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) /** \} */ -#ifdef WITH_XR_OPENXR - -static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) -{ - const bool session_exists = WM_xr_session_exists(xr_data); - - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { - if (slink->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)slink; - - if (v3d->flag & V3D_XR_SESSION_MIRROR) { - ED_view3d_xr_mirror_update(area, v3d, session_exists); - } - - if (session_exists) { - wmWindowManager *wm = bmain->wm.first; - const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); - - ED_view3d_xr_shading_update(wm, v3d, scene); - } - /* Ensure no 3D View is tagged as session root. */ - else { - v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; - } - } - } - } - } - - WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); -} - -static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) -{ - /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ - wm_xr_session_update_screen(G_MAIN, xr_data); -} - -static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - View3D *v3d = CTX_wm_view3d(C); - - /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ - if (wm_xr_init(wm) == false) { - return OPERATOR_CANCELLED; - } - - v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; - wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); - wm_xr_session_update_screen(bmain, &wm->xr); - - WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); - - return OPERATOR_FINISHED; -} - -static void WM_OT_xr_session_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle VR Session"; - ot->idname = "WM_OT_xr_session_toggle"; - ot->description = - "Open a view for use with virtual reality headsets, or close it if already " - "opened"; - - /* callbacks */ - ot->exec = wm_xr_session_toggle_exec; - ot->poll = ED_operator_view3d_active; - - /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the - * UI instead. Not meant as a permanent solution. */ - ot->flag = OPTYPE_INTERNAL; -} - -#endif /* WITH_XR_OPENXR */ - /* -------------------------------------------------------------------- */ /** \name Operator Registration & Keymaps * \{ */ @@ -3881,9 +3800,6 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); -#ifdef WITH_XR_OPENXR - WM_operatortype_append(WM_OT_xr_session_toggle); -#endif #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif @@ -3891,6 +3807,10 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); +#ifdef WITH_XR_OPENXR + wm_xr_operatortypes_register(); +#endif + /* gizmos */ WM_operatortype_append(GIZMOGROUP_OT_gizmo_select); WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index d1eb10787e2..40e4d905fcd 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -172,8 +172,10 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab /* wm_dropbox.c */ 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); -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect); +void wm_drags_draw(bContext *C, wmWindow *win); #ifdef __cplusplus } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 628d50f05bd..72d88bb3043 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -49,6 +49,16 @@ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) copy_v3_v3(r_mat[3], pose->position); } +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]) +{ + wm_xr_pose_to_mat(pose, r_mat); + + BLI_assert(scale > 0.0f); + mul_v3_fl(r_mat[0], scale); + mul_v3_fl(r_mat[1], scale); + mul_v3_fl(r_mat[2], scale); +} + void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) { float iquat[4]; @@ -57,15 +67,32 @@ void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); } +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]) +{ + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_imat, iquat); + + BLI_assert(scale > 0.0f); + scale = 1.0f / scale; + mul_v3_fl(r_imat[0], scale); + mul_v3_fl(r_imat[1], scale); + mul_v3_fl(r_imat[2], scale); + + translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); +} + static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, const XrSessionSettings *session_settings, - float r_view_mat[4][4], - float r_proj_mat[4][4]) + const wmXrSessionState *session_state, + float r_viewmat[4][4], + float r_projmat[4][4]) { GHOST_XrPose eye_pose; - float eye_inv[4][4], base_inv[4][4]; + float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4]; + /* Calculate inverse eye matrix. */ copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { @@ -76,12 +103,14 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, } wm_xr_pose_to_imat(&eye_pose, eye_inv); - /* Calculate the base pose matrix (in world space!). */ - wm_xr_pose_to_imat(&draw_data->base_pose, base_inv); - mul_m4_m4m4(r_view_mat, eye_inv, base_inv); + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv); + wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv); + mul_m4_m4m4(m, eye_inv, base_inv); + mul_m4_m4m4(r_viewmat, m, nav_inv); - perspective_m4_fov(r_proj_mat, + perspective_m4_fov(r_projmat, draw_view->fov.angle_left, draw_view->fov.angle_right, draw_view->fov.angle_up, @@ -131,7 +160,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) BLI_assert(WM_xr_session_is_ready(xr_data)); wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data); - wm_xr_draw_matrices_create(draw_data, draw_view, settings, viewmat, winmat); + wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat); wm_xr_session_state_update(settings, draw_data, draw_view, session_state); if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 2cd0ba5c056..7de1f254224 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -33,6 +33,8 @@ typedef struct wmXrSessionState { GHOST_XrPose viewer_pose; /** The last known view matrix, calculated from above's viewer pose. */ float viewer_viewmat[4][4]; + /** The last known viewer matrix, without navigation applied. */ + float viewer_mat_base[4][4]; float focal_len; /** Copy of XrSessionSettings.base_pose_ data to detect changes that need @@ -43,6 +45,8 @@ typedef struct wmXrSessionState { int prev_settings_flag; /** Copy of wmXrDrawData.base_pose. */ GHOST_XrPose prev_base_pose; + /** Copy of wmXrDrawData.base_scale. */ + float prev_base_scale; /** Copy of GHOST_XrDrawViewInfo.local_pose. */ GHOST_XrPose prev_local_pose; /** Copy of wmXrDrawData.eye_position_ofs. */ @@ -51,6 +55,15 @@ typedef struct wmXrSessionState { bool force_reset_to_base_pose; bool is_view_data_set; + /** Current navigation transforms. */ + GHOST_XrPose nav_pose; + float nav_scale; + /** Navigation transforms from the last actions sync, used to calculate the viewer/controller + * poses. */ + GHOST_XrPose nav_pose_prev; + float nav_scale_prev; + bool is_navigation_dirty; + /** Last known controller data. */ ListBase controllers; /* #wmXrController */ @@ -106,6 +119,8 @@ typedef struct wmXrDrawData { * space). With positional tracking enabled, it should be the same as the base pose, when * disabled it also contains a location delta from the moment the option was toggled. */ GHOST_XrPose base_pose; + /** Base scale (uniform, world space). */ + float base_scale; /** Offset to _substract_ from the OpenXR eye and viewer pose to get the wanted effective pose * (e.g. a pose exactly at the landmark position). */ float eye_position_ofs[3]; /* Local/view space. */ @@ -123,9 +138,11 @@ typedef struct wmXrController { /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; float grip_mat[4][4]; + float grip_mat_base[4][4]; /** Pose (in world space) that represents the controller's aiming source. */ GHOST_XrPose aim_pose; float aim_mat[4][4]; + float aim_mat_base[4][4]; /** Controller model. */ struct GPUBatch *model; @@ -192,7 +209,7 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime); void wm_xr_session_data_free(wmXrSessionState *state); wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, const wmXrRuntimeData *runtime_data); -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data); @@ -214,6 +231,8 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state); /* wm_xr_draw.c */ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c new file mode 100644 index 00000000000..36af0147cb8 --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -0,0 +1,1537 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR Operators + * + * Collection of XR-related operators. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "GHOST_Types.h" + +#include "GPU_immediate.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Operator Conditions + * \{ */ + +/* op->poll */ +static bool wm_xr_operator_sessionactive(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_session_is_ready(&wm->xr); +} + +static bool wm_xr_operator_test_event(const wmOperator *op, const wmEvent *event) +{ + if (event->type != EVT_XR_ACTION) { + return false; + } + + BLI_assert(event->custom == EVT_DATA_XR); + BLI_assert(event->customdata); + + wmXrActionData *actiondata = event->customdata; + return (actiondata->ot == op->type && + IDP_EqualsProperties(actiondata->op_properties, op->properties)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Toggle + * + * Toggles an XR session, creating an XR context if necessary. + * \{ */ + +static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) +{ + const bool session_exists = WM_xr_session_exists(xr_data); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { + if (slink->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)slink; + + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_view3d_xr_mirror_update(area, v3d, session_exists); + } + + if (session_exists) { + wmWindowManager *wm = bmain->wm.first; + const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); + + ED_view3d_xr_shading_update(wm, v3d, scene); + } + /* Ensure no 3D View is tagged as session root. */ + else { + v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; + } + } + } + } + } + + WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); +} + +static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) +{ + /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ + wm_xr_session_update_screen(G_MAIN, xr_data); +} + +static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); + + /* Lazily-create XR context - tries to dynamic-link to the runtime, + * reading `active_runtime.json`. */ + if (wm_xr_init(wm) == false) { + return OPERATOR_CANCELLED; + } + + v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; + wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); + wm_xr_session_update_screen(bmain, &wm->xr); + + WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_session_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle VR Session"; + ot->idname = "WM_OT_xr_session_toggle"; + ot->description = + "Open a view for use with virtual reality headsets, or close it if already " + "opened"; + + /* callbacks */ + ot->exec = wm_xr_session_toggle_exec; + ot->poll = ED_operator_view3d_active; + + /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the + * UI instead. Not meant as a permanent solution. */ + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Grab Utilities + * \{ */ + +typedef struct XrGrabData { + float mat_prev[4][4]; + float mat_other_prev[4][4]; + bool bimanual_prev; + bool loc_lock, locz_lock, rot_lock, rotz_lock, scale_lock; +} XrGrabData; + +static void wm_xr_grab_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrGrabData), __func__); +} + +static void wm_xr_grab_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata) +{ + XrGrabData *data = op->customdata; + + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + + if (actiondata->bimanual) { + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + data->bimanual_prev = true; + } + else { + data->bimanual_prev = false; + } +} + +static void orient_mat_z_normalized(float R[4][4], const float z_axis[3]) +{ + const float scale = len_v3(R[0]); + float x_axis[3], y_axis[3]; + + cross_v3_v3v3(y_axis, z_axis, R[0]); + normalize_v3(y_axis); + mul_v3_v3fl(R[1], y_axis, scale); + + cross_v3_v3v3(x_axis, R[1], z_axis); + normalize_v3(x_axis); + mul_v3_v3fl(R[0], x_axis, scale); + + mul_v3_v3fl(R[2], z_axis, scale); +} + +static void wm_xr_navlocks_apply(const float nav_mat[4][4], + const float nav_inv[4][4], + bool loc_lock, + bool locz_lock, + bool rotz_lock, + float r_prev[4][4], + float r_curr[4][4]) +{ + /* Locked in base pose coordinates. */ + float prev_base[4][4], curr_base[4][4]; + + mul_m4_m4m4(prev_base, nav_inv, r_prev); + mul_m4_m4m4(curr_base, nav_inv, r_curr); + + if (rotz_lock) { + const float z_axis[3] = {0.0f, 0.0f, 1.0f}; + orient_mat_z_normalized(prev_base, z_axis); + orient_mat_z_normalized(curr_base, z_axis); + } + + if (loc_lock) { + copy_v3_v3(curr_base[3], prev_base[3]); + } + else if (locz_lock) { + curr_base[3][2] = prev_base[3][2]; + } + + mul_m4_m4m4(r_prev, nav_mat, prev_base); + mul_m4_m4m4(r_curr, nav_mat, curr_base); +} + +/** + * Compute transformation delta for a one-handed grab interaction. + * + * \param actiondata: Contains current controller pose in world space. + * \param data: Contains previous controller pose in world space. + * + * The delta is computed as the difference between the current and previous + * controller poses i.e. delta = curr * prev^-1. + */ +static void wm_xr_grab_compute(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + + if (!data->rot_lock) { + copy_m4_m4(prev, data->mat_prev); + zero_v3(prev[3]); + quat_to_mat4(curr, actiondata->controller_rot); + } + else { + unit_m4(prev); + unit_m4(curr); + } + + if (!data->loc_lock || nav_lock) { + copy_v3_v3(prev[3], data->mat_prev[3]); + copy_v3_v3(curr[3], actiondata->controller_loc); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** + * Compute transformation delta for a two-handed (bimanual) grab interaction. + * + * \param actiondata: Contains current controller poses in world space. + * \param data: Contains previous controller poses in world space. + * + * The delta is computed as the difference (delta = curr * prev^-1) between the current + * and previous transformations, where the transformations themselves are determined as follows: + * - Translation: Averaged controller positions. + * - Rotation: Rotation of axis line between controllers. + * - Scale: Distance between controllers. + */ +static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + unit_m4(prev); + unit_m4(curr); + + if (!data->rot_lock) { + /* Rotation. */ + float x_axis_prev[3], x_axis_curr[3], y_axis_prev[3], y_axis_curr[3], z_axis_prev[3], + z_axis_curr[3]; + float m0[3][3], m1[3][3]; + quat_to_mat3(m0, actiondata->controller_rot); + quat_to_mat3(m1, actiondata->controller_rot_other); + + /* x-axis is the base line between the two controllers. */ + sub_v3_v3v3(x_axis_prev, data->mat_prev[3], data->mat_other_prev[3]); + sub_v3_v3v3(x_axis_curr, actiondata->controller_loc, actiondata->controller_loc_other); + /* y-axis is the average of the controllers' y-axes. */ + add_v3_v3v3(y_axis_prev, data->mat_prev[1], data->mat_other_prev[1]); + mul_v3_fl(y_axis_prev, 0.5f); + add_v3_v3v3(y_axis_curr, m0[1], m1[1]); + mul_v3_fl(y_axis_curr, 0.5f); + /* z-axis is the cross product of the two. */ + cross_v3_v3v3(z_axis_prev, x_axis_prev, y_axis_prev); + cross_v3_v3v3(z_axis_curr, x_axis_curr, y_axis_curr); + /* Fix the y-axis to be orthogonal. */ + cross_v3_v3v3(y_axis_prev, z_axis_prev, x_axis_prev); + cross_v3_v3v3(y_axis_curr, z_axis_curr, x_axis_curr); + /* Normalize. */ + normalize_v3_v3(prev[0], x_axis_prev); + normalize_v3_v3(prev[1], y_axis_prev); + normalize_v3_v3(prev[2], z_axis_prev); + normalize_v3_v3(curr[0], x_axis_curr); + normalize_v3_v3(curr[1], y_axis_curr); + normalize_v3_v3(curr[2], z_axis_curr); + } + + if (!data->loc_lock || nav_lock) { + /* Translation: translation of the averaged controller locations. */ + add_v3_v3v3(prev[3], data->mat_prev[3], data->mat_other_prev[3]); + mul_v3_fl(prev[3], 0.5f); + add_v3_v3v3(curr[3], actiondata->controller_loc, actiondata->controller_loc_other); + mul_v3_fl(curr[3], 0.5f); + } + + if (!data->scale_lock) { + /* Scaling: distance between controllers. */ + float scale, v[3]; + + sub_v3_v3v3(v, data->mat_prev[3], data->mat_other_prev[3]); + scale = len_v3(v); + mul_v3_fl(prev[0], scale); + mul_v3_fl(prev[1], scale); + mul_v3_fl(prev[2], scale); + + sub_v3_v3v3(v, actiondata->controller_loc, actiondata->controller_loc_other); + scale = len_v3(v); + mul_v3_fl(curr[0], scale); + mul_v3_fl(curr[1], scale); + mul_v3_fl(curr[2], scale); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Grab + * + * Navigates the scene by grabbing with XR controllers. + * \{ */ + +static int wm_xr_navigation_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + + wm_xr_grab_init(op); + wm_xr_grab_update(op, actiondata); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static bool wm_xr_navigation_grab_can_do_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both + controllers are pressed) and 2) bimanual interaction occurred on the last update. This second + part is needed to avoid "jumpy" navigation changes when transitioning from one-handed to + two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how navigation deltas + are calculated). */ + return (actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_bimanual_ending(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + return (!actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool bimanual) +{ + if (bimanual) { + return data->loc_lock && data->rot_lock && data->scale_lock; + } + /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ + return data->loc_lock && data->rot_lock; +} + +static void wm_xr_navigation_grab_apply(wmXrData *xr, + const wmXrActionData *actiondata, + const XrGrabData *data, + bool bimanual) +{ + GHOST_XrPose nav_pose; + float nav_scale; + float nav_mat[4][4], nav_inv[4][4], delta[4][4], out[4][4]; + + const bool need_navinv = (data->loc_lock || data->locz_lock || data->rotz_lock); + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + wm_xr_pose_scale_to_mat(&nav_pose, nav_scale, nav_mat); + if (need_navinv) { + wm_xr_pose_scale_to_imat(&nav_pose, nav_scale, nav_inv); + } + + if (bimanual) { + wm_xr_grab_compute_bimanual( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + else { + wm_xr_grab_compute( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + /* Limit scale to reasonable values. */ + nav_scale = len_v3(out[0]); + + if (!(nav_scale < xr->session_settings.clip_start || + nav_scale > xr->session_settings.clip_end)) { + WM_xr_session_state_nav_location_set(xr, out[3]); + if (!data->rot_lock) { + mat4_to_quat(nav_pose.orientation_quat, out); + normalize_qt(nav_pose.orientation_quat); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + if (!data->scale_lock && bimanual) { + WM_xr_session_state_nav_scale_set(xr, nav_scale); + } + } +} + +static void wm_xr_navigation_grab_bimanual_state_update(const wmXrActionData *actiondata, + XrGrabData *data) +{ + if (actiondata->bimanual) { + if (!data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + } + data->bimanual_prev = true; + } + else { + if (data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + } + data->bimanual_prev = false; + } +} + +static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + XrGrabData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + const bool do_bimanual = wm_xr_navigation_grab_can_do_bimanual(actiondata, data); + + data->loc_lock = RNA_boolean_get(op->ptr, "lock_location"); + data->locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + data->rot_lock = RNA_boolean_get(op->ptr, "lock_rotation"); + data->rotz_lock = RNA_boolean_get(op->ptr, "lock_rotation_z"); + data->scale_lock = RNA_boolean_get(op->ptr, "lock_scale"); + + /* Check if navigation is locked. */ + if (!wm_xr_navigation_grab_is_locked(data, do_bimanual)) { + /* Prevent unwanted snapping (i.e. "jumpy" navigation changes when transitioning from + two-handed to one-handed interaction) at the end of a bimanual interaction. */ + if (!wm_xr_navigation_grab_is_bimanual_ending(actiondata, data)) { + wm_xr_navigation_grab_apply(xr, actiondata, data, do_bimanual); + } + } + + wm_xr_navigation_grab_bimanual_state_update(actiondata, data); + + /* Note: KM_PRESS and KM_RELEASE are the only two values supported by XR events during event + dispatching (see #wm_xr_session_action_states_interpret()). For modal XR operators, modal + handling starts when an input is "pressed" (action state exceeds the action threshold) and + ends when the input is "released" (state falls below the threshold). */ + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: + wm_xr_grab_uninit(op); + return OPERATOR_FINISHED; + default: + BLI_assert_unreachable(); + wm_xr_grab_uninit(op); + return OPERATOR_CANCELLED; + } +} + +static void WM_OT_xr_navigation_grab(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Grab"; + ot->idname = "WM_OT_xr_navigation_grab"; + ot->description = "Navigate the VR scene by grabbing with controllers"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_grab_invoke; + ot->exec = wm_xr_navigation_grab_exec; + ot->modal = wm_xr_navigation_grab_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean( + ot->srna, "lock_location", false, "Lock Location", "Prevent changes to viewer location"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean( + ot->srna, "lock_rotation", false, "Lock Rotation", "Prevent changes to viewer rotation"); + RNA_def_boolean(ot->srna, + "lock_rotation_z", + false, + "Lock Up Orientation", + "Prevent changes to viewer up orientation"); + RNA_def_boolean(ot->srna, "lock_scale", false, "Lock Scale", "Prevent changes to viewer scale"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Raycast Utilities + * \{ */ + +static const float g_xr_default_raycast_axis[3] = {0.0f, 0.0f, -1.0f}; +static const float g_xr_default_raycast_color[4] = {0.35f, 0.35f, 1.0f, 1.0f}; + +typedef struct XrRaycastData { + bool from_viewer; + float origin[3]; + float direction[3]; + float end[3]; + float color[4]; + void *draw_handle; +} XrRaycastData; + +static void wm_xr_raycast_draw(const bContext *UNUSED(C), + ARegion *UNUSED(region), + void *customdata) +{ + const XrRaycastData *data = customdata; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (data->from_viewer) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4fv(data->color); + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_point_size(7.0f); + + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, data->end); + immEnd(); + } + else { + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + + immUniform1f("lineWidth", 3.0f * U.pixelsize); + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + + immBegin(GPU_PRIM_LINES, 2); + immAttrSkip(col); + immVertex3fv(pos, data->origin); + immAttr4fv(col, data->color); + immVertex3fv(pos, data->end); + immEnd(); + } + + immUnbindProgram(); +} + +static void wm_xr_raycast_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrRaycastData), __func__); + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (!st) { + return; + } + + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (!art) { + return; + } + + XrRaycastData *data = op->customdata; + data->draw_handle = ED_region_draw_cb_activate( + art, wm_xr_raycast_draw, op->customdata, REGION_DRAW_POST_VIEW); +} + +static void wm_xr_raycast_uninit(wmOperator *op) +{ + if (!op->customdata) { + return; + } + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + XrRaycastData *data = op->customdata; + ED_region_draw_cb_exit(art, data->draw_handle); + } + } + + MEM_freeN(op->customdata); +} + +static void wm_xr_raycast_update(wmOperator *op, + const wmXrData *xr, + const wmXrActionData *actiondata) +{ + XrRaycastData *data = op->customdata; + float ray_length, axis[3]; + + data->from_viewer = RNA_boolean_get(op->ptr, "from_viewer"); + RNA_float_get_array(op->ptr, "axis", axis); + RNA_float_get_array(op->ptr, "color", data->color); + + if (data->from_viewer) { + float viewer_rot[4]; + WM_xr_session_state_viewer_pose_location_get(xr, data->origin); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_rot); + mul_qt_v3(viewer_rot, axis); + ray_length = (xr->session_settings.clip_start + xr->session_settings.clip_end) / 2.0f; + } + else { + copy_v3_v3(data->origin, actiondata->controller_loc); + mul_qt_v3(actiondata->controller_rot, axis); + ray_length = xr->session_settings.clip_end; + } + + copy_v3_v3(data->direction, axis); + madd_v3_v3v3fl(data->end, data->origin, data->direction, ray_length); +} + +static void wm_xr_raycast(Scene *scene, + Depsgraph *depsgraph, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + float r_location[3], + float r_normal[3], + int *r_index, + Object **r_ob, + float r_obmat[4][4]) +{ + /* Uses same raycast method as Scene.ray_cast(). */ + SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, 0); + + ED_transform_snap_object_project_ray_ex( + sctx, + depsgraph, + NULL, + &(const struct SnapObjectParams){ + .snap_select = (selectable_only ? SNAP_SELECTABLE : SNAP_ALL)}, + origin, + direction, + ray_dist, + r_location, + r_normal, + r_index, + r_ob, + r_obmat); + + ED_transform_snap_object_context_destroy(sctx); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Fly + * + * Navigates the scene by moving/turning relative to navigation space or the XR viewer or + * controller. + * \{ */ + +#define XR_DEFAULT_FLY_SPEED_MOVE 0.054f +#define XR_DEFAULT_FLY_SPEED_TURN 0.03f + +typedef enum eXrFlyMode { + XR_FLY_FORWARD = 0, + XR_FLY_BACK = 1, + XR_FLY_LEFT = 2, + XR_FLY_RIGHT = 3, + XR_FLY_UP = 4, + XR_FLY_DOWN = 5, + XR_FLY_TURNLEFT = 6, + XR_FLY_TURNRIGHT = 7, + XR_FLY_VIEWER_FORWARD = 8, + XR_FLY_VIEWER_BACK = 9, + XR_FLY_VIEWER_LEFT = 10, + XR_FLY_VIEWER_RIGHT = 11, + XR_FLY_CONTROLLER_FORWARD = 12, +} eXrFlyMode; + +typedef struct XrFlyData { + float viewer_rot[4]; + double time_prev; +} XrFlyData; + +static void wm_xr_fly_init(wmOperator *op, const wmXrData *xr) +{ + BLI_assert(op->customdata == NULL); + + XrFlyData *data = op->customdata = MEM_callocN(sizeof(XrFlyData), __func__); + + WM_xr_session_state_viewer_pose_rotation_get(xr, data->viewer_rot); + data->time_prev = PIL_check_seconds_timer(); +} + +static void wm_xr_fly_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_fly_compute_move(eXrFlyMode mode, + float speed, + const float ref_quat[4], + const float nav_mat[4][4], + bool locz_lock, + float r_delta[4][4]) +{ + float ref_axes[3][3]; + quat_to_mat3(ref_axes, ref_quat); + + unit_m4(r_delta); + + switch (mode) { + /* Navigation space reference. */ + case XR_FLY_FORWARD: + madd_v3_v3fl(r_delta[3], ref_axes[1], speed); + return; + case XR_FLY_BACK: + madd_v3_v3fl(r_delta[3], ref_axes[1], -speed); + return; + case XR_FLY_LEFT: + madd_v3_v3fl(r_delta[3], ref_axes[0], -speed); + return; + case XR_FLY_RIGHT: + madd_v3_v3fl(r_delta[3], ref_axes[0], speed); + return; + case XR_FLY_UP: + case XR_FLY_DOWN: + if (!locz_lock) { + madd_v3_v3fl(r_delta[3], ref_axes[2], (mode == XR_FLY_UP) ? speed : -speed); + } + return; + /* Viewer/controller space reference. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_CONTROLLER_FORWARD: + negate_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_BACK: + copy_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_LEFT: + negate_v3_v3(r_delta[3], ref_axes[0]); + break; + case XR_FLY_VIEWER_RIGHT: + copy_v3_v3(r_delta[3], ref_axes[0]); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + return; + } + + if (locz_lock) { + /* Lock elevation in navigation space. */ + float z_axis[3], projected[3]; + + normalize_v3_v3(z_axis, nav_mat[2]); + project_v3_v3v3_normalized(projected, r_delta[3], z_axis); + sub_v3_v3(r_delta[3], projected); + + normalize_v3(r_delta[3]); + } + + mul_v3_fl(r_delta[3], speed); +} + +static void wm_xr_fly_compute_turn(eXrFlyMode mode, + float speed, + const float viewer_mat[4][4], + const float nav_mat[4][4], + const float nav_inv[4][4], + float r_delta[4][4]) +{ + BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + float z_axis[3], m[3][3], prev[4][4], curr[4][4]; + + /* Turn around Z-axis in navigation space. */ + normalize_v3_v3(z_axis, nav_mat[2]); + axis_angle_normalized_to_mat3(m, z_axis, (mode == XR_FLY_TURNLEFT) ? speed : -speed); + copy_m4_m3(r_delta, m); + + copy_m4_m4(prev, viewer_mat); + mul_m4_m4m4(curr, r_delta, viewer_mat); + + /* Lock location in base pose space. */ + wm_xr_navlocks_apply(nav_mat, nav_inv, true, false, false, prev, curr); + + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); +} + +static void wm_xr_basenav_rotation_calc(const wmXrData *xr, + const float nav_rotation[4], + float r_rotation[4]) +{ + /* Apply nav rotation to base pose Z-rotation. */ + float base_eul[3], base_quatz[4]; + quat_to_eul(base_eul, xr->runtime->session_state.prev_base_pose.orientation_quat); + axis_angle_to_quat_single(base_quatz, 'Z', base_eul[2]); + mul_qt_qtqt(r_rotation, nav_rotation, base_quatz); +} + +static int wm_xr_navigation_fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wmWindowManager *wm = CTX_wm_manager(C); + + wm_xr_fly_init(op, &wm->xr); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_fly_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_fly_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + if (event->val == KM_RELEASE) { + wm_xr_fly_uninit(op); + return OPERATOR_FINISHED; + } + + const wmXrActionData *actiondata = event->customdata; + XrFlyData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + eXrFlyMode mode; + bool turn, locz_lock, dir_lock, speed_frame_based; + bool speed_interp_cubic = false; + float speed, speed_max, speed_p0[2], speed_p1[2]; + GHOST_XrPose nav_pose; + float nav_mat[4][4], delta[4][4], out[4][4]; + + const double time_now = PIL_check_seconds_timer(); + + mode = (eXrFlyMode)RNA_enum_get(op->ptr, "mode"); + turn = (mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + dir_lock = RNA_boolean_get(op->ptr, "lock_direction"); + speed_frame_based = RNA_boolean_get(op->ptr, "speed_frame_based"); + speed = RNA_float_get(op->ptr, "speed_min"); + speed_max = RNA_float_get(op->ptr, "speed_max"); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "speed_interpolation0"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p0); + speed_interp_cubic = true; + } + else { + speed_p0[0] = speed_p0[1] = 0.0f; + } + + prop = RNA_struct_find_property(op->ptr, "speed_interpolation1"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p1); + speed_interp_cubic = true; + } + else { + speed_p1[0] = speed_p1[1] = 1.0f; + } + + /* Ensure valid interpolation. */ + if (speed_max < speed) { + speed_max = speed; + } + + /* Interpolate between min/max speeds based on button state. */ + switch (actiondata->type) { + case XR_BOOLEAN_INPUT: + speed = speed_max; + break; + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: { + float state = (actiondata->type == XR_FLOAT_INPUT) ? fabsf(actiondata->state[0]) : + len_v2(actiondata->state); + float speed_t = (actiondata->float_threshold < 1.0f) ? + (state - actiondata->float_threshold) / + (1.0f - actiondata->float_threshold) : + 1.0f; + if (speed_interp_cubic) { + float start[2], end[2], p[2]; + + start[0] = 0.0f; + start[1] = speed; + speed_p0[1] = speed + speed_p0[1] * (speed_max - speed); + speed_p1[1] = speed + speed_p1[1] * (speed_max - speed); + end[0] = 1.0f; + end[1] = speed_max; + + interp_v2_v2v2v2v2_cubic(p, start, speed_p0, speed_p1, end, speed_t); + speed = p[1]; + } + else { + speed += speed_t * (speed_max - speed); + } + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (!speed_frame_based) { + /* Adjust speed based on last update time. */ + speed *= time_now - data->time_prev; + } + data->time_prev = time_now; + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + wm_xr_pose_to_mat(&nav_pose, nav_mat); + + if (turn) { + if (dir_lock) { + unit_m4(delta); + } + else { + GHOST_XrPose viewer_pose; + float viewer_mat[4][4], nav_inv[4][4]; + + WM_xr_session_state_viewer_pose_location_get(xr, viewer_pose.position); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_pose.orientation_quat); + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + wm_xr_pose_to_imat(&nav_pose, nav_inv); + + wm_xr_fly_compute_turn(mode, speed, viewer_mat, nav_mat, nav_inv, delta); + } + } + else { + float nav_scale, ref_quat[4]; + + /* Adjust speed for base and navigation scale. */ + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + speed *= xr->session_settings.base_scale * nav_scale; + + switch (mode) { + /* Move relative to navigation space. */ + case XR_FLY_FORWARD: + case XR_FLY_BACK: + case XR_FLY_LEFT: + case XR_FLY_RIGHT: + case XR_FLY_UP: + case XR_FLY_DOWN: + wm_xr_basenav_rotation_calc(xr, nav_pose.orientation_quat, ref_quat); + break; + /* Move relative to viewer. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_VIEWER_BACK: + case XR_FLY_VIEWER_LEFT: + case XR_FLY_VIEWER_RIGHT: + if (dir_lock) { + copy_qt_qt(ref_quat, data->viewer_rot); + } + else { + WM_xr_session_state_viewer_pose_rotation_get(xr, ref_quat); + } + break; + /* Move relative to controller. */ + case XR_FLY_CONTROLLER_FORWARD: + copy_qt_qt(ref_quat, actiondata->controller_rot); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + break; + } + + wm_xr_fly_compute_move(mode, speed, ref_quat, nav_mat, locz_lock, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + WM_xr_session_state_nav_location_set(xr, out[3]); + if (turn) { + mat4_to_quat(nav_pose.orientation_quat, out); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + + if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_fly_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_navigation_fly(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Fly"; + ot->idname = "WM_OT_xr_navigation_fly"; + ot->description = "Move/turn relative to the VR viewer or controller"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_fly_invoke; + ot->exec = wm_xr_navigation_fly_exec; + ot->modal = wm_xr_navigation_fly_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static const EnumPropertyItem fly_modes[] = { + {XR_FLY_FORWARD, "FORWARD", 0, "Forward", "Move along navigation forward axis"}, + {XR_FLY_BACK, "BACK", 0, "Back", "Move along navigation back axis"}, + {XR_FLY_LEFT, "LEFT", 0, "Left", "Move along navigation left axis"}, + {XR_FLY_RIGHT, "RIGHT", 0, "Right", "Move along navigation right axis"}, + {XR_FLY_UP, "UP", 0, "Up", "Move along navigation up axis"}, + {XR_FLY_DOWN, "DOWN", 0, "Down", "Move along navigation down axis"}, + {XR_FLY_TURNLEFT, + "TURNLEFT", + 0, + "Turn Left", + "Turn counter-clockwise around navigation up axis"}, + {XR_FLY_TURNRIGHT, "TURNRIGHT", 0, "Turn Right", "Turn clockwise around navigation up axis"}, + {XR_FLY_VIEWER_FORWARD, + "VIEWER_FORWARD", + 0, + "Viewer Forward", + "Move along viewer's forward axis"}, + {XR_FLY_VIEWER_BACK, "VIEWER_BACK", 0, "Viewer Back", "Move along viewer's back axis"}, + {XR_FLY_VIEWER_LEFT, "VIEWER_LEFT", 0, "Viewer Left", "Move along viewer's left axis"}, + {XR_FLY_VIEWER_RIGHT, "VIEWER_RIGHT", 0, "Viewer Right", "Move along viewer's right axis"}, + {XR_FLY_CONTROLLER_FORWARD, + "CONTROLLER_FORWARD", + 0, + "Controller Forward", + "Move along controller's forward axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const float default_speed_p0[2] = {0.0f, 0.0f}; + static const float default_speed_p1[2] = {1.0f, 1.0f}; + + RNA_def_enum(ot->srna, "mode", fly_modes, XR_FLY_VIEWER_FORWARD, "Mode", "Fly mode"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean(ot->srna, + "lock_direction", + false, + "Lock Direction", + "Limit movement to viewer's intial direction"); + RNA_def_boolean(ot->srna, + "speed_frame_based", + true, + "Frame Based Speed", + "Apply fixed movement deltas every update"); + RNA_def_float(ot->srna, + "speed_min", + XR_DEFAULT_FLY_SPEED_MOVE / 3.0f, + 0.0f, + 1000.0f, + "Minimum Speed", + "Minimum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float(ot->srna, + "speed_max", + XR_DEFAULT_FLY_SPEED_MOVE, + 0.0f, + 1000.0f, + "Maximum Speed", + "Maximum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation0", + 2, + default_speed_p0, + 0.0f, + 1.0f, + "Speed Interpolation 0", + "First cubic spline control point between min/max speeds", + 0.0f, + 1.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation1", + 2, + default_speed_p1, + 0.0f, + 1.0f, + "Speed Interpolation 1", + "Second cubic spline control point between min/max speeds", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Teleport + * + * Casts a ray from an XR controller's pose and teleports to any hit geometry. + * \{ */ + +static void wm_xr_navigation_teleport(bContext *C, + wmXrData *xr, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + const bool teleport_axes[3], + float teleport_t, + float teleport_ofs) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + float location[3]; + float normal[3]; + int index; + Object *ob = NULL; + float obmat[4][4]; + + wm_xr_raycast(scene, + depsgraph, + origin, + direction, + ray_dist, + selectable_only, + location, + normal, + &index, + &ob, + obmat); + + /* Teleport. */ + if (ob) { + float nav_location[3], nav_rotation[4], viewer_location[3]; + float nav_axes[3][3], projected[3], v0[3], v1[3]; + float out[3] = {0.0f, 0.0f, 0.0f}; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_viewer_pose_location_get(xr, viewer_location); + + wm_xr_basenav_rotation_calc(xr, nav_rotation, nav_rotation); + quat_to_mat3(nav_axes, nav_rotation); + + /* Project locations onto navigation axes. */ + for (int a = 0; a < 3; ++a) { + project_v3_v3v3_normalized(projected, nav_location, nav_axes[a]); + if (teleport_axes[a]) { + /* Interpolate between projected locations. */ + project_v3_v3v3_normalized(v0, location, nav_axes[a]); + project_v3_v3v3_normalized(v1, viewer_location, nav_axes[a]); + sub_v3_v3(v0, v1); + madd_v3_v3fl(projected, v0, teleport_t); + /* Subtract offset. */ + project_v3_v3v3_normalized(v0, normal, nav_axes[a]); + madd_v3_v3fl(projected, v0, teleport_ofs); + } + /* Add to final location. */ + add_v3_v3(out, projected); + } + + WM_xr_session_state_nav_location_set(xr, out); + } +} + +static int wm_xr_navigation_teleport_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wm_xr_raycast_init(op); + + int retval = op->type->modal(C, op, event); + + if ((retval & OPERATOR_RUNNING_MODAL) != 0) { + WM_event_add_modal_handler(C, op); + } + + return retval; +} + +static int wm_xr_navigation_teleport_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + wm_xr_raycast_update(op, xr, actiondata); + + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: { + XrRaycastData *data = op->customdata; + bool selectable_only, teleport_axes[3]; + float teleport_t, teleport_ofs, ray_dist; + + RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes); + teleport_t = RNA_float_get(op->ptr, "interpolation"); + teleport_ofs = RNA_float_get(op->ptr, "offset"); + selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); + ray_dist = RNA_float_get(op->ptr, "distance"); + + wm_xr_navigation_teleport(C, + xr, + data->origin, + data->direction, + &ray_dist, + selectable_only, + teleport_axes, + teleport_t, + teleport_ofs); + + wm_xr_raycast_uninit(op); + + return OPERATOR_FINISHED; + } + default: + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_raycast_uninit(op); + return OPERATOR_CANCELLED; + } +} + +static void WM_OT_xr_navigation_teleport(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Teleport"; + ot->idname = "WM_OT_xr_navigation_teleport"; + ot->description = "Set VR viewer location to controller raycast hit location"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_teleport_invoke; + ot->exec = wm_xr_navigation_teleport_exec; + ot->modal = wm_xr_navigation_teleport_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static bool default_teleport_axes[3] = {true, true, true}; + + RNA_def_boolean_vector(ot->srna, + "teleport_axes", + 3, + default_teleport_axes, + "Teleport Axes", + "Enabled teleport axes in navigation space"); + RNA_def_float(ot->srna, + "interpolation", + 1.0f, + 0.0f, + 1.0f, + "Interpolation", + "Interpolation factor between viewer and hit locations", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "offset", + 0.0f, + 0.0f, + FLT_MAX, + "Offset", + "Offset along hit normal to subtract from final location", + 0.0f, + FLT_MAX); + RNA_def_boolean(ot->srna, + "selectable_only", + true, + "Selectable Only", + "Only allow selectable objects to influence raycast result"); + RNA_def_float(ot->srna, + "distance", + BVH_RAYCAST_DIST_MAX, + 0.0, + BVH_RAYCAST_DIST_MAX, + "", + "Maximum raycast distance", + 0.0, + BVH_RAYCAST_DIST_MAX); + RNA_def_boolean( + ot->srna, "from_viewer", false, "From Viewer", "Use viewer pose as raycast origin"); + RNA_def_float_vector(ot->srna, + "axis", + 3, + g_xr_default_raycast_axis, + -1.0f, + 1.0f, + "Axis", + "Raycast axis in controller/viewer space", + -1.0f, + 1.0f); + RNA_def_float_color(ot->srna, + "color", + 4, + g_xr_default_raycast_color, + 0.0f, + 1.0f, + "Color", + "Raycast color", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Reset + * + * Resets XR navigation deltas relative to session base pose. + * \{ */ + +static int wm_xr_navigation_reset_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + bool reset_loc, reset_rot, reset_scale; + + reset_loc = RNA_boolean_get(op->ptr, "location"); + reset_rot = RNA_boolean_get(op->ptr, "rotation"); + reset_scale = RNA_boolean_get(op->ptr, "scale"); + + if (reset_loc) { + float loc[3]; + if (!reset_scale) { + float nav_rotation[4], nav_scale; + + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Adjust location based on scale. */ + mul_v3_v3fl(loc, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(loc, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, loc); + negate_v3(loc); + } + else { + zero_v3(loc); + } + WM_xr_session_state_nav_location_set(xr, loc); + } + + if (reset_rot) { + float rot[4]; + unit_qt(rot); + WM_xr_session_state_nav_rotation_set(xr, rot); + } + + if (reset_scale) { + if (!reset_loc) { + float nav_location[3], nav_rotation[4], nav_scale; + float nav_axes[3][3], v[3]; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Offset any location changes when changing scale. */ + mul_v3_v3fl(v, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(v, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, v); + add_v3_v3(nav_location, v); + + /* Reset elevation to base pose value. */ + quat_to_mat3(nav_axes, nav_rotation); + project_v3_v3v3_normalized(v, nav_location, nav_axes[2]); + sub_v3_v3(nav_location, v); + + WM_xr_session_state_nav_location_set(xr, nav_location); + } + WM_xr_session_state_nav_scale_set(xr, 1.0f); + } + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_navigation_reset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Reset"; + ot->idname = "WM_OT_xr_navigation_reset"; + ot->description = "Reset VR navigation deltas relative to session base pose"; + + /* callbacks */ + ot->exec = wm_xr_navigation_reset_exec; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean(ot->srna, "location", true, "Location", "Reset location deltas"); + RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "Reset rotation deltas"); + RNA_def_boolean(ot->srna, "scale", true, "Scale", "Reset scale deltas"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Registration + * \{ */ + +void wm_xr_operatortypes_register(void) +{ + WM_operatortype_append(WM_OT_xr_session_toggle); + WM_operatortype_append(WM_OT_xr_navigation_grab); + WM_operatortype_append(WM_OT_xr_navigation_fly); + WM_operatortype_append(WM_OT_xr_navigation_teleport); + WM_operatortype_append(WM_OT_xr_navigation_reset); +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 9f53db1347c..3224869b04a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -66,11 +66,20 @@ static void wm_xr_session_create_cb(void) Main *bmain = G_MAIN; wmWindowManager *wm = bmain->wm.first; wmXrData *xr_data = &wm->xr; + wmXrSessionState *state = &xr_data->runtime->session_state; + XrSessionSettings *settings = &xr_data->session_settings; /* Get action set data from Python. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE); wm_xr_session_actions_init(xr_data); + + /* Initialize navigation. */ + WM_xr_session_state_navigation_reset(state); + if (settings->base_scale < FLT_EPSILON) { + settings->base_scale = 1.0f; + } + state->prev_base_scale = settings->base_scale; } static void wm_xr_session_controller_data_free(wmXrSessionState *state) @@ -128,8 +137,10 @@ void wm_xr_session_toggle(wmWindowManager *wm, wmXrData *xr_data = &wm->xr; if (WM_xr_session_exists(xr_data)) { - GHOST_XrSessionEnd(xr_data->runtime->context); + /* Must set first, since #GHOST_XrSessionEnd() may immediately free the runtime. */ xr_data->runtime->session_state.is_started = false; + + GHOST_XrSessionEnd(xr_data->runtime->context); } else { GHOST_XrSessionBeginInfo begin_info; @@ -167,7 +178,8 @@ bool WM_xr_session_is_ready(const wmXrData *xr) static void wm_xr_session_base_pose_calc(const Scene *scene, const XrSessionSettings *settings, - GHOST_XrPose *r_base_pose) + GHOST_XrPose *r_base_pose, + float *r_base_scale) { const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) && settings->base_pose_object) ? @@ -198,6 +210,8 @@ static void wm_xr_session_base_pose_calc(const Scene *scene, copy_v3_fl(r_base_pose->position, 0.0f); axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2); } + + *r_base_scale = settings->base_scale; } static void wm_xr_session_draw_data_populate(wmXrData *xr_data, @@ -213,7 +227,8 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data, r_draw_data->xr_data = xr_data; r_draw_data->surface_data = g_xr_surface->customdata; - wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose); + wm_xr_session_base_pose_calc( + r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale); } wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, @@ -291,7 +306,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState return SESSION_STATE_EVENT_NONE; } -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data) @@ -319,6 +334,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, else { copy_v3_fl(draw_data->eye_position_ofs, 0.0f); } + /* Reset navigation. */ + WM_xr_session_state_navigation_reset(state); break; case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE: if (use_position_tracking) { @@ -348,29 +365,32 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, wmXrSessionState *state) { GHOST_XrPose viewer_pose; - const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; - const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING; - - mul_qt_qtqt(viewer_pose.orientation_quat, - draw_data->base_pose.orientation_quat, - draw_view->local_pose.orientation_quat); - copy_v3_v3(viewer_pose.position, draw_data->base_pose.position); - /* The local pose and the eye pose (which is copied from an earlier local pose) both are view - * space, so Y-up. In this case we need them in regular Z-up. */ - if (use_position_tracking) { - viewer_pose.position[0] += draw_view->local_pose.position[0]; - viewer_pose.position[1] -= draw_view->local_pose.position[2]; - viewer_pose.position[2] += draw_view->local_pose.position[1]; - } - if (!use_absolute_tracking) { - viewer_pose.position[0] -= draw_data->eye_position_ofs[0]; - viewer_pose.position[1] += draw_data->eye_position_ofs[2]; - viewer_pose.position[2] -= draw_data->eye_position_ofs[1]; - } - - copy_v3_v3(state->viewer_pose.position, viewer_pose.position); - copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); - wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat); + float viewer_mat[4][4], base_mat[4][4], nav_mat[4][4]; + + /* Calculate viewer matrix. */ + copy_qt_qt(viewer_pose.orientation_quat, draw_view->local_pose.orientation_quat); + if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { + zero_v3(viewer_pose.position); + } + else { + copy_v3_v3(viewer_pose.position, draw_view->local_pose.position); + } + if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) { + sub_v3_v3(viewer_pose.position, draw_data->eye_position_ofs); + } + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_mat(&draw_data->base_pose, draw_data->base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose_prev, state->nav_scale_prev, nav_mat); + mul_m4_m4m4(state->viewer_mat_base, base_mat, viewer_mat); + mul_m4_m4m4(viewer_mat, nav_mat, state->viewer_mat_base); + + /* Save final viewer pose and viewmat. */ + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, draw_data->base_scale * state->nav_scale_prev, state->viewer_viewmat); + /* No idea why, but multiplying by two seems to make it match the VR view more. */ state->focal_len = 2.0f * fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left, @@ -378,7 +398,10 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose)); + state->prev_base_scale = draw_data->base_scale; memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose)); + copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); + state->prev_settings_flag = settings->flag; state->prev_base_pose_type = settings->base_pose_type; state->prev_base_pose_object = settings->base_pose_object; @@ -503,6 +526,74 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, return true; } +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, xr->runtime->session_state.nav_pose.position); + return true; +} + +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]) +{ + if (WM_xr_session_exists(xr)) { + copy_v3_v3(xr->runtime->session_state.nav_pose.position, location); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_qt(r_rotation); + return false; + } + + copy_qt_qt(r_rotation, xr->runtime->session_state.nav_pose.orientation_quat); + return true; +} + +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]) +{ + if (WM_xr_session_exists(xr)) { + BLI_ASSERT_UNIT_QUAT(rotation); + copy_qt_qt(xr->runtime->session_state.nav_pose.orientation_quat, rotation); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + *r_scale = 1.0f; + return false; + } + + *r_scale = xr->runtime->session_state.nav_scale; + return true; +} + +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale) +{ + if (WM_xr_session_exists(xr)) { + /* Clamp to reasonable values. */ + CLAMP(scale, xr->session_settings.clip_start, xr->session_settings.clip_end); + xr->runtime->session_state.nav_scale = scale; + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +void WM_xr_session_state_navigation_reset(wmXrSessionState *state) +{ + zero_v3(state->nav_pose.position); + unit_qt(state->nav_pose.orientation_quat); + state->nav_scale = 1.0f; + state->is_navigation_dirty = true; +} + /* -------------------------------------------------------------------- */ /** \name XR-Session Actions * @@ -522,16 +613,21 @@ void wm_xr_session_actions_init(wmXrData *xr) static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, const float view_ofs[3], const float base_mat[4][4], + const float nav_mat[4][4], GHOST_XrPose *r_pose, - float r_mat[4][4]) + float r_mat[4][4], + float r_mat_base[4][4]) { float m[4][4]; /* Calculate controller matrix in world space. */ wm_xr_pose_to_mat(raw_pose, m); - /* Apply eye position and base pose offsets. */ + /* Apply eye position offset. */ sub_v3_v3(m[3], view_ofs); - mul_m4_m4m4(r_mat, base_mat, m); + + /* Apply base pose and navigation. */ + mul_m4_m4m4(r_mat_base, base_mat, m); + mul_m4_m4m4(r_mat, nav_mat, r_mat_base); /* Save final pose. */ mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); @@ -547,7 +643,7 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); unsigned int subaction_idx = 0; - float view_ofs[3], base_mat[4][4]; + float view_ofs[3], base_mat[4][4], nav_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { copy_v3_v3(view_ofs, state->prev_local_pose.position); @@ -559,19 +655,24 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin add_v3_v3(view_ofs, state->prev_eye_position_ofs); } - wm_xr_pose_to_mat(&state->prev_base_pose, base_mat); + wm_xr_pose_scale_to_mat(&state->prev_base_pose, state->prev_base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, nav_mat); LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) { wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->grip_pose, - controller->grip_mat); + controller->grip_mat, + controller->grip_mat_base); wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->aim_pose, - controller->aim_mat); + controller->aim_mat, + controller->aim_mat_base); if (!controller->model) { /* Notify GHOST to load/continue loading the controller model data. This can be called more @@ -1094,10 +1195,26 @@ void wm_xr_session_actions_update(wmWindowManager *wm) return; } + XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; wmXrSessionState *state = &xr->runtime->session_state; wmXrActionSet *active_action_set = state->active_action_set; + if (state->is_navigation_dirty) { + memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev)); + state->nav_scale_prev = state->nav_scale; + state->is_navigation_dirty = false; + + /* Update viewer pose with any navigation changes since the last actions sync so that data + * is correct for queries. */ + float m[4][4], viewer_mat[4][4]; + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, m); + mul_m4_m4m4(viewer_mat, m, state->viewer_mat_base); + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); + } + int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); if (!ret) { return; @@ -1108,7 +1225,7 @@ void wm_xr_session_actions_update(wmWindowManager *wm) wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(&xr->session_settings, + wm_xr_session_controller_data_update(settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, xr_context, diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h index 0f0fbe8bc00..caba6038c56 100644 --- a/source/blender/windowmanager/xr/wm_xr.h +++ b/source/blender/windowmanager/xr/wm_xr.h @@ -30,3 +30,6 @@ bool wm_xr_init(wmWindowManager *wm); void wm_xr_exit(wmWindowManager *wm); void wm_xr_session_toggle(wmWindowManager *wm, wmWindow *win, wmXrSessionExitFn session_exit_fn); bool wm_xr_events_handle(wmWindowManager *wm); + +/* wm_xr_operators.c */ +void wm_xr_operatortypes_register(void); |