diff options
Diffstat (limited to 'source/blender')
762 files changed, 21577 insertions, 15710 deletions
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 7e92f79a523..eb3f9805240 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -271,7 +271,7 @@ void BLF_state_print(int fontid); #define BLF_ROTATION (1 << 0) #define BLF_CLIPPING (1 << 1) #define BLF_SHADOW (1 << 2) -#define BLF_KERNING_DEFAULT (1 << 3) +// #define BLF_FLAG_UNUSED_3 (1 << 3) /* dirty */ #define BLF_MATRIX (1 << 4) #define BLF_ASPECT (1 << 5) #define BLF_WORD_WRAP (1 << 6) diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 9168e7aa19c..2f9eb0753ac 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -108,7 +108,6 @@ void BLF_cache_clear(void) FontBLF *font = global_font[i]; if (font) { blf_glyph_cache_clear(font); - blf_kerning_cache_clear(font); } } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 53c4135254a..75a2e893119 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -72,6 +72,38 @@ static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; /* -------------------------------------------------------------------- */ +/** \name FreeType Utilities (Internal) + * \{ */ + +/** + * Convert a FreeType 26.6 value representing an unscaled design size to pixels. + * This is an exact copy of the scaling done inside FT_Get_Kerning when called + * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts. + */ +static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) +{ + /* Scale value by font size using integer-optimized multiplication. */ + FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + + /* FreeType states that this '25' has been determined heuristically. */ + if (font->face->size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + } + + /* Copies of internal FreeType macros needed here. */ +#define FT_PIX_FLOOR(x) ((x) & ~63) +#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32) + + /* Round to even 64ths, then divide by 64. */ + return (int)FT_PIX_ROUND(scaled) >> 6; + +#undef FT_PIX_FLOOR +#undef FT_PIX_ROUND +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Glyph Batching * \{ */ @@ -257,151 +289,78 @@ static void blf_batch_draw_end(void) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Glyph Stepping Utilities (Internal) + * \{ */ -int blf_font_init(void) -{ - memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); -} - -void blf_font_exit(void) -{ - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); - blf_batch_draw_exit(); -} +/* Fast path for runs of ASCII characters. Given that common UTF-8 + * input will consist of an overwhelming majority of ASCII + * characters. + */ -void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) +BLI_INLINE GlyphBLF *blf_utf8_next_fast( + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t *i_p, uint *r_c) { - GlyphCacheBLF *gc; - FT_Error err; - - blf_glyph_cache_acquire(font); - - gc = blf_glyph_cache_find(font, size, dpi); - if (gc) { - /* Optimization: do not call FT_Set_Char_Size if size did not change. */ - if (font->size == size && font->dpi == dpi) { - blf_glyph_cache_release(font); - return; + 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)++; } - - err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); - if (err) { - /* FIXME: here we can go through the fixed size and choice a close one */ - printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); - - blf_glyph_cache_release(font); - return; + else if ((*r_c = BLI_str_utf8_as_unicode_step(str, i_p)) != BLI_UTF8_ERR) { + 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); + } } - - font->size = size; - font->dpi = dpi; - - if (!gc) { - blf_glyph_cache_new(font); + else { + g = NULL; } - blf_glyph_cache_release(font); + return g; } -static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc) +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) { - GlyphBLF **glyph_ascii_table; - - glyph_ascii_table = gc->glyph_ascii_table; - - /* build ascii on demand */ - if (glyph_ascii_table['0'] == NULL) { - GlyphBLF *g; - for (uint i = 0; i < 256; i++) { - g = blf_glyph_search(gc, i); - if (!g) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - glyph_ascii_table[i] = g; - } + if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { + return; } - return glyph_ascii_table; -} + FT_Vector delta = {KERNING_ENTRY_UNSET}; -static void blf_font_ensure_ascii_kerning(FontBLF *font, - GlyphCacheBLF *gc, - const FT_UInt kern_mode) -{ - KerningCacheBLF *kc = font->kerning_cache; + /* 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]; + } - font->kerning_mode = kern_mode; + /* If not ASCII or not found in cache, ask FreeType for kerning. */ + if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + /* Note that this function sets delta values to zero on any error. */ + FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); + } - if (!kc || kc->mode != kern_mode) { - font->kerning_cache = kc = blf_kerning_cache_find(font); - if (!kc) { - font->kerning_cache = kc = blf_kerning_cache_new(font, gc); - } + /* 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 (delta.x != 0) { + /* Convert unscaled design units to pixels and move pen. */ + *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); } } -/* Fast path for runs of ASCII characters. Given that common UTF-8 - * input will consist of an overwhelming majority of ASCII - * characters. - */ +/** \} */ -/* NOTE: `blf_font_ensure_ascii_table(font, gc);` must be called before this macro. */ - -#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c, _glyph_ascii_table) \ - if (((_c) = (_str)[_i]) < 0x80) { \ - _g = (_glyph_ascii_table)[_c]; \ - _i++; \ - } \ - else if ((_c = BLI_str_utf8_as_unicode_step(_str, &(_i))) != BLI_UTF8_ERR) { \ - if ((_g = blf_glyph_search(_gc, _c)) == NULL) { \ - _g = blf_glyph_add(_font, _gc, FT_Get_Char_Index((_font)->face, _c), _c); \ - } \ - } \ - else { \ - _g = NULL; \ - } \ - (void)0 - -#define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \ - const bool _has_kerning = FT_HAS_KERNING((_font)->face) != 0; \ - const FT_UInt _kern_mode = (_has_kerning == 0) ? 0 : \ - (((_font)->flags & BLF_KERNING_DEFAULT) ? \ - ft_kerning_default : \ - (FT_UInt)FT_KERNING_UNFITTED) - -/* NOTE: `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this macro. */ - -#define BLF_KERNING_STEP_FAST(_font, _kern_mode, _g_prev, _g, _c_prev, _c, _pen_x) \ - { \ - if (_g_prev) { \ - FT_Vector _delta; \ - if (_c_prev < 0x80 && _c < 0x80) { \ - _pen_x += (_font)->kerning_cache->table[_c][_c_prev]; \ - } \ - else if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == \ - 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 - -#define BLF_KERNING_STEP(_font, _kern_mode, _g_prev, _g, _delta, _pen_x) \ - { \ - if (_g_prev) { \ - _delta.x = _delta.y = 0; \ - if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == 0) { \ - _pen_x += (int)_delta.x >> 6; \ - } \ - } \ - } \ - (void)0 +/* -------------------------------------------------------------------- */ +/** \name Text Drawing: GPU + * \{ */ static void blf_font_draw_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -420,16 +379,10 @@ static void blf_font_draw_ex(FontBLF *font, return; } - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -437,9 +390,7 @@ static void blf_font_draw_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -472,22 +423,20 @@ static void blf_font_draw_ascii_ex( int pen_x = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); blf_batch_draw_begin(font); while ((c = *(str++)) && len--) { - BLI_assert(c < 128); - if ((g = glyph_ascii_table[c]) == NULL) { - continue; - } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); + BLI_assert(c < GLYPH_ASCII_TABLE_SIZE); + g = gc->glyph_ascii_table[c]; + if (UNLIKELY(g == NULL)) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index((font)->face, c), c); + gc->glyph_ascii_table[c] = g; + if (UNLIKELY(g == NULL)) { + continue; + } } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /* do not return this loop if clipped, we want every character tested */ blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); @@ -522,12 +471,11 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) size_t i = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); blf_batch_draw_begin(font); while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -554,6 +502,12 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth) return columns; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Drawgin: Buffer + * \{ */ + /* Sanity checks are done by BLF_draw_buffer() */ static void blf_font_draw_buffer_ex(FontBLF *font, GlyphCacheBLF *gc, @@ -568,8 +522,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int pen_y_basis = (int)font->pos[1] + pen_y; size_t i = 0; - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - /* buffer specific vars */ FontBufInfoBLF *buf_info = &font->buf_info; const float *b_col_float = buf_info->col_float; @@ -577,14 +529,10 @@ static void blf_font_draw_buffer_ex(FontBLF *font, int chx, chy; int y, x; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - /* another buffer specific call for color conversion */ while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -592,9 +540,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -707,9 +653,17 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res blf_glyph_cache_release(font); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Width to Sting Length + * + * Use to implement exported functions: + * - #BLF_width_to_strlen + * - #BLF_width_to_rstrlen + * \{ */ + static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const bool has_kerning, - const FT_UInt kern_mode, const uint c_prev, const uint c, GlyphBLF *g_prev, @@ -723,9 +677,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, *pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); *pen_x += g->advance_i; @@ -741,21 +693,13 @@ size_t blf_font_width_to_strlen( size_t i, i_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i]; i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); - if (blf_font_width_to_strlen_glyph_process( - font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -778,15 +722,8 @@ size_t blf_font_width_to_rstrlen( char *s, *s_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); const int width_i = (int)width; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - if (has_kerning) { - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - } - i = BLI_strnlen(str, len); s = BLI_str_find_prev_char_utf8(str, &str[i]); i = (size_t)((s != NULL) ? s - str : 0); @@ -794,7 +731,7 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0); i_tmp = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i_tmp, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i_tmp, &c); 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) { s_prev = BLI_str_find_prev_char_utf8(str, s); @@ -802,12 +739,11 @@ size_t blf_font_width_to_rstrlen( if (s_prev != NULL) { i_tmp = i_prev; - BLF_UTF8_NEXT_FAST(font, gc, g_prev, str, i_tmp, c_prev, glyph_ascii_table); + g_prev = blf_utf8_next_fast(font, gc, str, &i_tmp, &c_prev); BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process( - font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { break; } } @@ -820,6 +756,12 @@ size_t blf_font_width_to_rstrlen( return i; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Glyph Bound Box with Callback + * \{ */ + static void blf_font_boundbox_ex(FontBLF *font, GlyphCacheBLF *gc, const char *str, @@ -832,22 +774,15 @@ static void blf_font_boundbox_ex(FontBLF *font, GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; - - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - rctf gbox; - BLF_KERNING_VARS(font, has_kerning, kern_mode); - box->xmin = 32000.0f; box->xmax = -32000.0f; box->ymin = 32000.0f; box->ymax = -32000.0f; - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - while ((i < len) && str[i]) { - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -855,9 +790,7 @@ static void blf_font_boundbox_ex(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -903,8 +836,165 @@ void blf_font_boundbox( blf_glyph_cache_release(font); } +void blf_font_width_and_height(FontBLF *font, + const char *str, + size_t len, + float *r_width, + float *r_height, + struct ResultBLF *r_info) +{ + float xa, ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + ya = font->aspect[1]; + } + else { + xa = 1.0f; + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + *r_width = (BLI_rctf_size_x(&box) * xa); + *r_height = (BLI_rctf_size_y(&box) * ya); +} + +float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float xa; + rctf box; + + if (font->flags & BLF_ASPECT) { + xa = font->aspect[0]; + } + else { + xa = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_x(&box) * xa; +} + +float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +{ + float ya; + rctf box; + + if (font->flags & BLF_ASPECT) { + ya = font->aspect[1]; + } + else { + ya = 1.0f; + } + + if (font->flags & BLF_WORD_WRAP) { + blf_font_boundbox__wrap(font, str, len, &box, r_info); + } + else { + blf_font_boundbox(font, str, len, &box, r_info); + } + return BLI_rctf_size_y(&box) * ya; +} + +float blf_font_fixed_width(FontBLF *font) +{ + const unsigned int c = ' '; + + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + GlyphBLF *g = blf_glyph_search(gc, c); + if (!g) { + g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); + + /* if we don't find the glyph. */ + if (!g) { + blf_glyph_cache_release(font); + return 0.0f; + } + } + + blf_glyph_cache_release(font); + return g->advance; +} + +static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, + GlyphCacheBLF *gc, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + 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; + rcti gbox; + + if (len == 0) { + /* early output. */ + return; + } + + while ((i < len) && str[i]) { + i_curr = i; + g = blf_utf8_next_fast(font, gc, str, &i, &c); + + 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); + + gbox.xmin = pen_x; + gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); + gbox.ymin = pen_y; + gbox.ymax = gbox.ymin - g->dims[1]; + + pen_x += g->advance_i; + + if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { + break; + } + + g_prev = g; + c_prev = c; + } + + if (r_info) { + r_info->lines = 1; + r_info->width = pen_x; + } +} +void blf_font_boundbox_foreach_glyph(FontBLF *font, + const char *str, + size_t len, + BLF_GlyphBoundsFn user_fn, + void *user_data, + struct ResultBLF *r_info) +{ + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); + blf_glyph_cache_release(font); +} + +/** \} */ + /* -------------------------------------------------------------------- */ -/** \name Word-Wrap Support +/** \name Text Evaluation: Word-Wrap with Callback * \{ */ /** @@ -928,18 +1018,14 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c; + unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; - FT_Vector delta; int pen_x = 0, pen_y = 0; size_t i = 0; int lines = 0; int pen_x_next = 0; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); struct WordWrapVars { int wrap_width; @@ -953,7 +1039,7 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); + g = blf_utf8_next_fast(font, gc, str, &i, &c); if (UNLIKELY(c == BLI_UTF8_ERR)) { break; @@ -961,9 +1047,7 @@ static void blf_font_wrap_apply(FontBLF *font, if (UNLIKELY(g == NULL)) { continue; } - if (has_kerning) { - BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x); - } + blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); /** * Implementation Detail (utf8). @@ -1003,12 +1087,14 @@ 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); @@ -1078,224 +1164,120 @@ void blf_font_draw_buffer__wrap(FontBLF *font, /** \} */ -void blf_font_width_and_height(FontBLF *font, - const char *str, - size_t len, - float *r_width, - float *r_height, - struct ResultBLF *r_info) +/* -------------------------------------------------------------------- */ +/** \name Text Evaluation: Count Missing Characters + * \{ */ + +int blf_font_count_missing_chars(FontBLF *font, + const char *str, + const size_t len, + int *r_tot_chars) { - float xa, ya; - rctf box; + int missing = 0; + size_t i = 0; - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - ya = font->aspect[1]; - } - else { - xa = 1.0f; - ya = 1.0f; - } + *r_tot_chars = 0; + while (i < len) { + unsigned int c; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); + if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) { + i++; + } + else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { + if (FT_Get_Char_Index((font)->face, c) == 0) { + missing++; + } + } + (*r_tot_chars)++; } - *r_width = (BLI_rctf_size_x(&box) * xa); - *r_height = (BLI_rctf_size_y(&box) * ya); + return missing; } -float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) -{ - float xa; - rctf box; - - if (font->flags & BLF_ASPECT) { - xa = font->aspect[0]; - } - else { - xa = 1.0f; - } +/** \} */ - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_x(&box) * xa; -} +/* -------------------------------------------------------------------- */ +/** \name Font Query: Attributes + * \{ */ -float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info) +int blf_font_height_max(FontBLF *font) { - float ya; - rctf box; + int height_max; - if (font->flags & BLF_ASPECT) { - ya = font->aspect[1]; - } - else { - ya = 1.0f; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + height_max = gc->glyph_height_max; - if (font->flags & BLF_WORD_WRAP) { - blf_font_boundbox__wrap(font, str, len, &box, r_info); - } - else { - blf_font_boundbox(font, str, len, &box, r_info); - } - return BLI_rctf_size_y(&box) * ya; + blf_glyph_cache_release(font); + return height_max; } -float blf_font_fixed_width(FontBLF *font) +int blf_font_width_max(FontBLF *font) { - const unsigned int c = ' '; + int width_max; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } + width_max = gc->glyph_width_max; blf_glyph_cache_release(font); - return g->advance; + return width_max; } -/* -------------------------------------------------------------------- */ -/** \name Glyph Bound Box with Callback - * \{ */ - -static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, - GlyphCacheBLF *gc, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info, - int pen_y) +float blf_font_descender(FontBLF *font) { - unsigned int c, c_prev = BLI_UTF8_ERR; - GlyphBLF *g, *g_prev = NULL; - int pen_x = 0; - size_t i = 0, i_curr; - rcti gbox; - - if (len == 0) { - /* early output. */ - return; - } - - GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc); - - BLF_KERNING_VARS(font, has_kerning, kern_mode); - - blf_font_ensure_ascii_kerning(font, gc, kern_mode); - - while ((i < len) && str[i]) { - i_curr = i; - BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table); - - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } - if (UNLIKELY(g == NULL)) { - continue; - } - if (has_kerning) { - BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); - } + float descender; - gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); - gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->dims[1]; + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + descender = gc->descender; - pen_x += g->advance_i; + blf_glyph_cache_release(font); + return descender; +} - if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { - break; - } +float blf_font_ascender(FontBLF *font) +{ + float ascender; - g_prev = g; - c_prev = c; - } + GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); + ascender = gc->ascender; - if (r_info) { - r_info->lines = 1; - r_info->width = pen_x; - } + blf_glyph_cache_release(font); + return ascender; } -void blf_font_boundbox_foreach_glyph(FontBLF *font, - const char *str, - size_t len, - BLF_GlyphBoundsFn user_fn, - void *user_data, - struct ResultBLF *r_info) + +char *blf_display_name(FontBLF *font) { - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0); - blf_glyph_cache_release(font); + if (!font->face->family_name) { + return NULL; + } + return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); } /** \} */ -int blf_font_count_missing_chars(FontBLF *font, - const char *str, - const size_t len, - int *r_tot_chars) -{ - int missing = 0; - size_t i = 0; - - *r_tot_chars = 0; - while (i < len) { - unsigned int c; +/* -------------------------------------------------------------------- */ +/** \name Font Subsystem Init/Exit + * \{ */ - if ((c = str[i]) < 0x80) { - i++; - } - else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) { - if (FT_Get_Char_Index((font)->face, c) == 0) { - missing++; - } - } - (*r_tot_chars)++; - } - return missing; +int blf_font_init(void) +{ + memset(&g_batch, 0, sizeof(g_batch)); + BLI_spin_init(&ft_lib_mutex); + BLI_spin_init(&blf_glyph_cache_mutex); + return FT_Init_FreeType(&ft_lib); } -void blf_font_free(FontBLF *font) +void blf_font_exit(void) { - BLI_spin_lock(&blf_glyph_cache_mutex); - GlyphCacheBLF *gc; - - while ((gc = BLI_pophead(&font->cache))) { - blf_glyph_cache_free(gc); - } - - blf_kerning_cache_clear(font); + FT_Done_FreeType(ft_lib); + BLI_spin_end(&ft_lib_mutex); + BLI_spin_end(&blf_glyph_cache_mutex); + blf_batch_draw_exit(); +} - FT_Done_Face(font->face); - if (font->filename) { - MEM_freeN(font->filename); - } - if (font->name) { - MEM_freeN(font->name); - } - MEM_freeN(font); +/** \} */ - BLI_spin_unlock(&blf_glyph_cache_mutex); -} +/* -------------------------------------------------------------------- */ +/** \name Font New/Free + * \{ */ static void blf_font_fill(FontBLF *font) { @@ -1324,7 +1306,6 @@ static void blf_font_fill(FontBLF *font) font->dpi = 0; font->size = 0; BLI_listbase_clear(&font->cache); - BLI_listbase_clear(&font->kerning_caches); font->kerning_cache = NULL; #if BLF_BLUR_ENABLE font->blur = 0; @@ -1359,9 +1340,15 @@ FontBLF *blf_font_new(const char *name, const char *filename) return NULL; } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); + err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); if (err) { - printf("Can't set the unicode character map!\n"); + err = FT_Select_Charmap(font->face, FT_ENCODING_APPLE_ROMAN); + } + if (err && font->face->num_charmaps > 0) { + err = FT_Select_Charmap(font->face, font->face->charmaps[0]->encoding); + } + if (err) { + printf("Can't set a character map!\n"); FT_Done_Face(font->face); MEM_freeN(font); return NULL; @@ -1379,6 +1366,17 @@ FontBLF *blf_font_new(const char *name, const char *filename) font->name = BLI_strdup(name); font->filename = BLI_strdup(filename); blf_font_fill(font); + + if (FT_HAS_KERNING(font->face)) { + /* Create kerning cache table and fill with value indicating "unset". */ + font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); + for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { + for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) { + font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET; + } + } + } + return font; } @@ -1418,58 +1416,61 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m return font; } -int blf_font_height_max(FontBLF *font) +void blf_font_free(FontBLF *font) { - int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - height_max = gc->glyph_height_max; + BLI_spin_lock(&blf_glyph_cache_mutex); + GlyphCacheBLF *gc; - blf_glyph_cache_release(font); - return height_max; -} + while ((gc = BLI_pophead(&font->cache))) { + blf_glyph_cache_free(gc); + } -int blf_font_width_max(FontBLF *font) -{ - int width_max; + if (font->kerning_cache) { + MEM_freeN(font->kerning_cache); + } - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - width_max = gc->glyph_width_max; + FT_Done_Face(font->face); + if (font->filename) { + MEM_freeN(font->filename); + } + if (font->name) { + MEM_freeN(font->name); + } + MEM_freeN(font); - blf_glyph_cache_release(font); - return width_max; + BLI_spin_unlock(&blf_glyph_cache_mutex); } -float blf_font_descender(FontBLF *font) -{ - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - descender = gc->descender; +/** \} */ - blf_glyph_cache_release(font); - return descender; -} +/* -------------------------------------------------------------------- */ +/** \name Font Configure + * \{ */ -float blf_font_ascender(FontBLF *font) +void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) { - float ascender; + blf_glyph_cache_acquire(font); - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - blf_font_ensure_ascii_table(font, gc); - ascender = gc->ascender; + GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi); + if (gc && (font->size == size && font->dpi == dpi)) { + /* Optimization: do not call FT_Set_Char_Size if size did not change. */ + } + else { + const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi); + if (err) { + /* FIXME: here we can go through the fixed size and choice a close one */ + printf("The current font don't support the size, %u and dpi, %u\n", size, dpi); + } + else { + font->size = size; + font->dpi = dpi; + if (gc == NULL) { + blf_glyph_cache_new(font); + } + } + } blf_glyph_cache_release(font); - return ascender; } -char *blf_display_name(FontBLF *font) -{ - if (!font->face->family_name) { - return NULL; - } - return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); -} +/** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 3f01501fda4..6cdf5fc5996 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -55,64 +55,6 @@ #include "BLI_math_vector.h" #include "BLI_strict_flags.h" -KerningCacheBLF *blf_kerning_cache_find(FontBLF *font) -{ - KerningCacheBLF *p; - - p = (KerningCacheBLF *)font->kerning_caches.first; - while (p) { - if (p->mode == font->kerning_mode) { - return p; - } - p = p->next; - } - return NULL; -} - -/* Create a new glyph cache for the current kerning mode. */ -KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc) -{ - KerningCacheBLF *kc; - - kc = (KerningCacheBLF *)MEM_callocN(sizeof(KerningCacheBLF), "blf_kerning_cache_new"); - kc->next = NULL; - kc->prev = NULL; - kc->mode = font->kerning_mode; - - unsigned int i, j; - for (i = 0; i < 0x80; i++) { - for (j = 0; j < 0x80; j++) { - GlyphBLF *g = blf_glyph_search(gc, i); - if (!g) { - FT_UInt glyph_index = FT_Get_Char_Index(font->face, i); - g = blf_glyph_add(font, gc, glyph_index, i); - } - /* Can fail on certain fonts */ - GlyphBLF *g_prev = blf_glyph_search(gc, j); - - FT_Vector delta = { - .x = 0, - .y = 0, - }; - if (g && g_prev && FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) { - kc->table[i][j] = (int)delta.x >> 6; - } - else { - kc->table[i][j] = 0; - } - } - } - - BLI_addhead(&font->kerning_caches, kc); - return kc; -} - -void blf_kerning_cache_clear(FontBLF *font) -{ - font->kerning_cache = NULL; - BLI_freelistN(&font->kerning_caches); -} - GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi) { GlyphCacheBLF *p; @@ -144,8 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->glyphs_len_max = (int)font->face->num_glyphs; - gc->glyphs_len_free = (int)font->face->num_glyphs; gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; @@ -514,7 +454,6 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl memcpy(&gc->bitmap_result[gc->bitmap_len], g->bitmap, (size_t)buff_size); gc->bitmap_len = bitmap_len; - gc->glyphs_len_free--; g->glyph_cache = gc; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 35a6d019eac..ab2a26b1e06 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -121,10 +121,6 @@ int blf_font_count_missing_chars(struct FontBLF *font, void blf_font_free(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font); -struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc); -void blf_kerning_cache_clear(struct FontBLF *font); - struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font, unsigned int size, unsigned int dpi); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 36bb8769306..38d7d7b6e21 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -28,6 +28,15 @@ #define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */ +/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */ +#define GLYPH_ASCII_TABLE_SIZE 128 + +/* Number of characters in KerningCacheBLF.table. */ +#define KERNING_CACHE_TABLE_SIZE 128 + +/* A value in the kerning cache that indicates it is not yet set. */ +#define KERNING_ENTRY_UNSET INT_MAX + typedef struct BatchBLF { struct FontBLF *font; /* can only batch glyph from the same font */ struct GPUBatch *batch; @@ -44,14 +53,11 @@ typedef struct BatchBLF { extern BatchBLF g_batch; typedef struct KerningCacheBLF { - struct KerningCacheBLF *next, *prev; - - /* kerning mode. */ - FT_UInt mode; - - /* only cache a ascii glyph pairs. Only store the x - * offset we are interested in, instead of the full FT_Vector. */ - int table[0x80][0x80]; + /** + * Cache a ascii glyph pairs. Only store the x offset we are interested in, + * instead of the full #FT_Vector since it's not used for drawing at the moment. + */ + int ascii_table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE]; } KerningCacheBLF; typedef struct GlyphCacheBLF { @@ -71,7 +77,7 @@ typedef struct GlyphCacheBLF { ListBase bucket[257]; /* fast ascii lookup */ - struct GlyphBLF *glyph_ascii_table[256]; + struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE]; /* texture array, to draw the glyphs. */ GPUTexture *texture; @@ -84,12 +90,6 @@ typedef struct GlyphCacheBLF { int glyph_width_max; int glyph_height_max; - /* number of glyphs in the font. */ - int glyphs_len_max; - - /* number of glyphs not yet loaded (decreases every glyph loaded). */ - int glyphs_len_free; - /* ascender and descender value. */ float ascender; float descender; @@ -99,7 +99,7 @@ typedef struct GlyphBLF { struct GlyphBLF *next; struct GlyphBLF *prev; - /* and the character, as UTF8 */ + /* and the character, as UTF-32 */ unsigned int c; /* freetype2 index, to speed-up the search. */ @@ -225,10 +225,7 @@ typedef struct FontBLF { */ ListBase cache; - /* list of kerning cache for this font. */ - ListBase kerning_caches; - - /* current kerning cache for this font and kerning mode. */ + /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */ KerningCacheBLF *kerning_cache; /* freetype2 lib handle. */ @@ -240,9 +237,6 @@ typedef struct FontBLF { /* freetype2 face. */ FT_Face face; - /* freetype kerning */ - FT_UInt kerning_mode; - /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e76e3ed8fe0..4ed4225c836 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 15 +#define BLENDER_FILE_SUBVERSION 18 /* 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_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 8be2fcbdb83..06be8ec80fc 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -69,7 +69,7 @@ typedef struct BVHTreeFromMesh { BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; - /* Vertex array, so that callbacks have instante access to data */ + /* Vertex array, so that callbacks have instant access to data. */ const struct MVert *vert; const struct MEdge *edge; /* only used for BVHTreeFromMeshEdges */ const struct MFace *face; diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h index a6b2aa8540a..836597f95da 100644 --- a/source/blender/blenkernel/BKE_cachefile.h +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -32,6 +32,7 @@ struct CacheReader; struct Depsgraph; struct Main; struct Object; +struct Scene; void BKE_cachefiles_init(void); void BKE_cachefiles_exit(void); @@ -60,6 +61,10 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file, const char *object_path); void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader); +bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file, + struct Scene *scene, + const int dag_eval_mode); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 0326386e5c1..2c7143be60e 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -65,7 +65,7 @@ void BKE_collection_add_from_collection(struct Main *bmain, struct Scene *scene, struct Collection *collection_src, struct Collection *collection_dst); -void BKE_collection_free(struct Collection *collection); +void BKE_collection_free_data(struct Collection *collection); bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy); struct Collection *BKE_collection_duplicate(struct Main *bmain, @@ -197,13 +197,14 @@ typedef void (*BKE_scene_collections_Cb)(struct Collection *ob, void *data); #define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode) \ { \ int _base_flag = (_mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER; \ - int _object_restrict_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_RESTRICT_VIEWPORT : \ - OB_RESTRICT_RENDER; \ + int _object_visibility_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_HIDE_VIEWPORT : \ + OB_HIDE_RENDER; \ int _base_id = 0; \ for (Base *_base = (Base *)BKE_collection_object_cache_get(_collection).first; _base; \ _base = _base->next, _base_id++) { \ Object *_object = _base->object; \ - if ((_base->flag & _base_flag) && (_object->restrictflag & _object_restrict_flag) == 0) { + if ((_base->flag & _base_flag) && \ + (_object->visibility_flag & _object_visibility_flag) == 0) { #define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END \ } \ diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index 575df93a9fc..784b395dfa5 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -192,6 +192,28 @@ bool BKE_constraint_remove_ex(ListBase *list, bool clear_dep); bool BKE_constraint_remove(ListBase *list, struct bConstraint *con); +bool BKE_constraint_apply_for_object(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bConstraint *con); +bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph, + struct Scene *scene, + ListBase /*bConstraint*/ *constraints, + struct Object *ob, + struct bConstraint *con); + +bool BKE_constraint_apply_for_pose(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bPoseChannel *pchan, + struct bConstraint *con); +bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph, + struct Scene *scene, + ListBase /*bConstraint*/ *constraints, + struct Object *ob, + struct bConstraint *con, + struct bPoseChannel *pchan); + void BKE_constraint_panel_expand(struct bConstraint *con); /* Constraints + Proxies function prototypes */ diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 8917580689d..eda6a03fa1a 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -360,7 +360,7 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); -const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C); +const struct AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C); struct AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid); bool CTX_wm_interface_locked(const bContext *C); diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 69c950a86dc..89713e9ad0a 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -70,6 +70,7 @@ typedef struct Global { * * -16384 and below: Reserved for python (add-ons) usage. * * -1: Disable faster motion paths computation (since 08/2018). * * 1 - 30: EEVEE debug/stats values (01/2018). + * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). @@ -144,7 +145,8 @@ enum { G_DEBUG_DEPSGRAPH_TIME = (1 << 11), /* depsgraph timing statistics and messages */ G_DEBUG_DEPSGRAPH_NO_THREADS = (1 << 12), /* single threaded depsgraph */ G_DEBUG_DEPSGRAPH_PRETTY = (1 << 13), /* use pretty colors in depsgraph messages */ - G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* use pretty colors in depsgraph messages */ + G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* Verify validness of session-wide identifiers + * assigned to ID datablocks */ G_DEBUG_DEPSGRAPH = (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_EVAL | G_DEBUG_DEPSGRAPH_TAG | G_DEBUG_DEPSGRAPH_TIME | G_DEBUG_DEPSGRAPH_UUID), G_DEBUG_SIMDATA = (1 << 15), /* sim debug data display */ diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 92e70b41e7b..b58317f4815 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -93,7 +93,7 @@ void BKE_gpencil_free_stroke(struct bGPDstroke *gps); bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); void BKE_gpencil_free_frames(struct bGPDlayer *gpl); void BKE_gpencil_free_layers(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd, bool free_all); +void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all); void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval); void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl); void BKE_gpencil_tag(struct bGPdata *gpd); diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index c1ccae7a437..29e3a74b1b2 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -169,7 +169,8 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain, const float matrix[4][4], const int frame_offset, const bool use_seams, - const bool use_faces); + const bool use_faces, + const bool use_vgroups); void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd, struct bGPDstroke *gps, diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index d298e5dcf6d..b62ad3ad24a 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,7 +45,7 @@ struct StampData; struct anim; #define IMA_MAX_SPACE 64 -#define IMA_UDIM_MAX 1999 +#define IMA_UDIM_MAX 2000 void BKE_images_init(void); void BKE_images_exit(void); @@ -56,7 +56,7 @@ void BKE_image_free_buffers(struct Image *image); void BKE_image_free_buffers_ex(struct Image *image, bool do_lock); void BKE_image_free_gputextures(struct Image *ima); /* call from library */ -void BKE_image_free(struct Image *image); +void BKE_image_free_data(struct Image *image); typedef void(StampCallback)(void *data, const char *propname, char *propvalue, int len); @@ -308,6 +308,8 @@ void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label); bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); +void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number); +void BKE_image_sort_tiles(struct Image *ima); bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile, diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 70d65e02246..cb4fc607703 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -36,7 +36,7 @@ struct Object; extern "C" { #endif -void BKE_key_free(struct Key *key); +void BKE_key_free_data(struct Key *key); void BKE_key_free_nolib(struct Key *key); struct Key *BKE_key_add(struct Main *bmain, struct ID *id); void BKE_key_sort(struct Key *key); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index fac5dc8c010..bb875f8d1c9 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -152,8 +152,6 @@ void BKE_libblock_copy_ex(struct Main *bmain, const int orig_flag); void *BKE_libblock_copy(struct Main *bmain, const struct ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Special version: used by data-block localization. */ -void *BKE_libblock_copy_for_localize(const struct ID *id); void BKE_libblock_rename(struct Main *bmain, struct ID *id, const char *name) ATTR_NONNULL(); void BLI_libblock_ensure_unique_name(struct Main *bmain, const char *name) ATTR_NONNULL(); @@ -201,6 +199,8 @@ enum { void BKE_libblock_free_datablock(struct ID *id, const int flag) ATTR_NONNULL(); void BKE_libblock_free_data(struct ID *id, const bool do_id_user) ATTR_NONNULL(); +void BKE_libblock_free_data_py(struct ID *id); + void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, const bool use_flag_from_idtag); void BKE_id_free(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index e3be9cd8ef8..ae464a48e9e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -95,7 +95,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh, const struct MLoopTri *looptri, int r_edges[3]); -void BKE_mesh_free(struct Mesh *me); +void BKE_mesh_free_data_for_undo(struct Mesh *me); void BKE_mesh_clear_geometry(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *me_src); @@ -280,39 +280,22 @@ void BKE_mesh_recalc_looptri_with_normals(const struct MLoop *mloop, /* *** mesh_normals.cc *** */ -void BKE_mesh_calc_normals_mapping_simple(struct Mesh *me); -void BKE_mesh_calc_normals_mapping(struct MVert *mverts, - int numVerts, - const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const struct MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3]); -void BKE_mesh_calc_normals_mapping_ex(struct MVert *mverts, - int numVerts, - const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const struct MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3], - const bool only_face_normals); -void BKE_mesh_calc_normals_poly(struct MVert *mverts, - float (*r_vertnors)[3], - int numVerts, +void BKE_mesh_normals_tag_dirty(struct Mesh *mesh); +void BKE_mesh_calc_normals_poly(const struct MVert *mvert, + int mvert_len, const struct MLoop *mloop, - const struct MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const bool only_face_normals); + int mloop_len, + const struct MPoly *mpoly, + int mpoly_len, + float (*r_poly_normals)[3]); +void BKE_mesh_calc_normals_poly_and_vertex(struct MVert *mvert, + int mvert_len, + const struct MLoop *mloop, + int mloop_len, + const struct MPoly *mpolys, + int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]); void BKE_mesh_calc_normals(struct Mesh *me); void BKE_mesh_ensure_normals(struct Mesh *me); void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh); diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index 5887db59ff2..80ced9b5f57 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -23,10 +23,6 @@ * \ingroup bke */ -#ifdef WITH_OPENVDB -# include "openvdb_capi.h" -#endif - #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 0b4e1191956..8be563e4c96 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -319,8 +319,10 @@ typedef struct ModifierTypeInfo { * changes. * * This function is optional (assumes false if not present). + * + * The dag_eval_mode should be of type eEvaluationMode. */ - bool (*dependsOnTime)(struct ModifierData *md); + bool (*dependsOnTime)(struct Scene *scene, struct ModifierData *md, const int dag_eval_mode); /** * True when a deform modifier uses normals, the requiredDataMask @@ -425,7 +427,7 @@ void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target) void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, const int flag); -bool BKE_modifier_depends_ontime(struct ModifierData *md); +bool BKE_modifier_depends_ontime(struct Scene *scene, struct ModifierData *md, int dag_eval_mode); bool BKE_modifier_supports_mapping(struct ModifierData *md); bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md); bool BKE_modifier_couldbe_cage(struct Scene *scene, struct ModifierData *md); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index cecb3118038..c4393246926 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate { #ifdef __cplusplus namespace blender { namespace nodes { -class SocketMFNetworkBuilder; -class NodeMFNetworkBuilder; +class NodeMultiFunctionBuilder; class GeoNodeExecParams; } // namespace nodes namespace fn { @@ -121,17 +120,20 @@ class MFDataType; } // namespace fn } // namespace blender -using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); +using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder); +using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)(); +using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, + void *r_value); #else -typedef void *NodeExpandInMFNetworkFunction; -typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; +typedef void *SocketGetGeometryNodesCPPTypeFunction; +typedef void *SocketGetGeometryNodesCPPValueFunction; typedef void *SocketGetCPPValueFunction; #endif @@ -191,12 +193,14 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); - /* Expands the socket into a multi-function node that outputs the socket value. */ - SocketExpandInMFNetworkFunction expand_in_mf_network; /* Return the CPPType of this socket. */ - SocketGetCPPTypeFunction get_cpp_type; + SocketGetCPPTypeFunction get_base_cpp_type; /* Get the value of this socket in a generic way. */ - SocketGetCPPValueFunction get_cpp_value; + SocketGetCPPValueFunction get_base_cpp_value; + /* Get geometry nodes cpp type. */ + SocketGetGeometryNodesCPPTypeFunction get_geometry_nodes_cpp_type; + /* Get geometry nodes cpp value. */ + SocketGetGeometryNodesCPPValueFunction get_geometry_nodes_cpp_value; } bNodeSocketType; typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context, @@ -323,8 +327,8 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; - /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ - NodeExpandInMFNetworkFunction expand_in_mf_network; + /* Build a multi-function for this node. */ + NodeMultiFunctionBuildFunction build_multi_function; /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; @@ -1466,6 +1470,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 #define GEO_NODE_CURVE_SET_HANDLES 1072 +#define GEO_NODE_CURVE_SPLINE_TYPE 1073 +#define GEO_NODE_CURVE_SELECT_HANDLES 1074 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 4724e6dfab6..a823602e341 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -401,7 +401,10 @@ void BKE_object_groups_clear(struct Main *bmain, struct Scene *scene, struct Obj struct KDTree_3d *BKE_object_as_kdtree(struct Object *ob, int *r_tot); -bool BKE_object_modifier_use_time(struct Object *ob, struct ModifierData *md); +bool BKE_object_modifier_use_time(struct Scene *scene, + struct Object *ob, + struct ModifierData *md, + int dag_eval_mode); bool BKE_object_modifier_update_subframe(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 2c0c6989acf..380f9045520 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -74,11 +74,13 @@ struct Ocean *BKE_ocean_add(void); void BKE_ocean_free_data(struct Ocean *oc); void BKE_ocean_free(struct Ocean *oc); bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution); -void BKE_ocean_init_from_modifier(struct Ocean *ocean, +bool BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd, const int resolution); -void BKE_ocean_init(struct Ocean *o, +bool BKE_ocean_is_valid(const struct Ocean *o); + +bool BKE_ocean_init(struct Ocean *o, int M, int N, float Lx, diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 3f99a0dc793..c83fca767a1 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -302,7 +302,7 @@ void BKE_ptcache_remove(void); /************ ID specific functions ************************/ void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra); -int BKE_ptcache_id_exist(PTCacheID *id, int cfra); +bool BKE_ptcache_id_exist(PTCacheID *id, int cfra); int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode); void BKE_ptcache_id_time(PTCacheID *pid, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 6d58e165ea3..83ce5e72794 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -174,6 +174,10 @@ bool BKE_scene_uses_blender_eevee(const struct Scene *scene); bool BKE_scene_uses_blender_workbench(const struct Scene *scene); bool BKE_scene_uses_cycles(const struct Scene *scene); +/* Return whether the Cycles experimental feature is enabled. It is invalid to call without first + * ensuring that Cycles is the active render engine (e.g. with BKE_scene_uses_cycles). */ +bool BKE_scene_uses_cycles_experimental_features(struct Scene *scene); + void BKE_scene_copy_data_eevee(struct Scene *sce_dst, const struct Scene *sce_src); void BKE_scene_disable_color_management(struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 0b08bbfeff5..6f341a12b82 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -473,7 +473,7 @@ void BKE_screen_view3d_shading_init(struct View3DShading *shading); /* screen */ void BKE_screen_foreach_id_screen_area(struct LibraryForeachIDData *data, struct ScrArea *area); -void BKE_screen_free(struct bScreen *screen); +void BKE_screen_free_data(struct bScreen *screen); void BKE_screen_area_map_free(struct ScrAreaMap *area_map) ATTR_NONNULL(); struct ScrEdge *BKE_screen_find_edge(const struct bScreen *screen, diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 57ce33a239f..4b257b3b8ab 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -97,6 +97,7 @@ typedef struct SoundInfo { eSoundChannels channels; } specs; float length; + double start_offset; } SoundInfo; /* Get information about given sound. Returns truth on success., false if sound can not be loaded @@ -139,8 +140,12 @@ void BKE_sound_remove_scene_sound(struct Scene *scene, void *handle); void BKE_sound_mute_scene_sound(void *handle, char mute); -void BKE_sound_move_scene_sound( - struct Scene *scene, void *handle, int startframe, int endframe, int frameskip); +void BKE_sound_move_scene_sound(struct Scene *scene, + void *handle, + int startframe, + int endframe, + int frameskip, + double audio_offset); void BKE_sound_move_scene_sound_defaults(struct Scene *scene, struct Sequence *sequence); void BKE_sound_update_scene_sound(void *handle, struct bSound *sound); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 53485ecbd6b..fc145f1ddf1 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -109,6 +109,7 @@ class Spline { SplinePtr copy() const; SplinePtr copy_only_settings() const; SplinePtr copy_without_attributes() const; + static void copy_base_settings(const Spline &src, Spline &dst); Spline::Type type() const; @@ -209,8 +210,6 @@ class Spline { virtual void correct_end_tangents() const = 0; virtual void copy_settings(Spline &dst) const = 0; virtual void copy_data(Spline &dst) const = 0; - - static void copy_base_settings(const Spline &src, Spline &dst); }; /** diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 78bfe8c9afb..adaef22d5bc 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -698,6 +698,13 @@ if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) endif() +if(WITH_USD) + list(APPEND INC + ../io/usd + ) + add_definitions(-DWITH_USD) +endif() + if(WITH_OPENSUBDIV) list(APPEND INC_SYS ${OPENSUBDIV_INCLUDE_DIRS} diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index ba8cf8debe9..59e81938e79 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -360,7 +360,7 @@ void DM_init(DerivedMesh *dm, dm->needsFree = 1; dm->dirty = (DMDirtyFlag)0; - /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */ + /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1); copy_vn_i(dm->edgeData.typemap, CD_NUMTYPES, -1); copy_vn_i(dm->faceData.typemap, CD_NUMTYPES, -1); @@ -816,15 +816,14 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input, if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { float(*polynors)[3] = (float(*)[3])CustomData_add_layer( &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); - BKE_mesh_calc_normals_poly(mesh_final->mvert, - nullptr, - mesh_final->totvert, - mesh_final->mloop, - mesh_final->mpoly, - mesh_final->totloop, - mesh_final->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert, + mesh_final->totvert, + mesh_final->mloop, + mesh_final->totloop, + mesh_final->mpoly, + mesh_final->totpoly, + polynors, + nullptr); } } @@ -1536,15 +1535,14 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) { float(*polynors)[3] = (float(*)[3])CustomData_add_layer( &mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly); - BKE_mesh_calc_normals_poly(mesh_final->mvert, - nullptr, - mesh_final->totvert, - mesh_final->mloop, - mesh_final->mpoly, - mesh_final->totloop, - mesh_final->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert, + mesh_final->totvert, + mesh_final->mloop, + mesh_final->totloop, + mesh_final->mpoly, + mesh_final->totpoly, + polynors, + nullptr); } } diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index d55f023d209..981815f400a 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -186,22 +186,21 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) static void action_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bAction *act = (bAction *)id; - if (act->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, bAction, id_address, &act->id); - BKE_id_blend_write(writer, &act->id); - BKE_fcurve_blend_write(writer, &act->curves); + BLO_write_id_struct(writer, bAction, id_address, &act->id); + BKE_id_blend_write(writer, &act->id); - LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { - BLO_write_struct(writer, bActionGroup, grp); - } + BKE_fcurve_blend_write(writer, &act->curves); - LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { - BLO_write_struct(writer, TimeMarker, marker); - } + LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { + BLO_write_struct(writer, bActionGroup, grp); + } - BKE_previewimg_blend_write(writer, act->preview); + LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { + BLO_write_struct(writer, TimeMarker, marker); } + + BKE_previewimg_blend_write(writer, act->preview); } static void action_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fae75762cde..92b0db5b214 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -426,7 +426,7 @@ bool BKE_animsys_rna_path_resolve(PointerRNA *ptr, } /* less than 1.0 evaluates to false, use epsilon to avoid float error */ -#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > ((1.0f - FLT_EPSILON))) +#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > (1.0f - FLT_EPSILON)) bool BKE_animsys_read_from_rna_path(PathResolvedRNA *anim_rna, float *r_value) { @@ -2625,7 +2625,7 @@ static void animsys_create_action_track_strip(const AnimData *adt, bAction *action = adt->action; - if ((adt->flag & ADT_NLA_EDIT_ON)) { + if (adt->flag & ADT_NLA_EDIT_ON) { action = adt->tmpact; } @@ -2671,7 +2671,7 @@ static void animsys_create_action_track_strip(const AnimData *adt, static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt) { /* Skip disabled tracks unless it contains the tweaked strip. */ - const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && + const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && adt->act_track && (nlt->index == adt->act_track->index); if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) { return false; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index b8ed519e8d1..87320c88b1b 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -212,25 +212,24 @@ static void write_bone(BlendWriter *writer, Bone *bone) static void armature_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bArmature *arm = (bArmature *)id; - if (arm->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - arm->bonehash = NULL; - arm->edbo = NULL; - /* Must always be cleared (armatures don't have their own edit-data). */ - arm->needs_flush_to_id = 0; - arm->act_edbone = NULL; - BLO_write_id_struct(writer, bArmature, id_address, &arm->id); - BKE_id_blend_write(writer, &arm->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + arm->bonehash = NULL; + arm->edbo = NULL; + /* Must always be cleared (armatures don't have their own edit-data). */ + arm->needs_flush_to_id = 0; + arm->act_edbone = NULL; - if (arm->adt) { - BKE_animdata_blend_write(writer, arm->adt); - } + BLO_write_id_struct(writer, bArmature, id_address, &arm->id); + BKE_id_blend_write(writer, &arm->id); - /* Direct data */ - LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { - write_bone(writer, bone); - } + if (arm->adt) { + BKE_animdata_blend_write(writer, arm->adt); + } + + /* Direct data */ + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { + write_bone(writer, bone); } } @@ -1882,7 +1881,7 @@ void BKE_bone_parent_transform_invert(struct BoneParentTransform *bpt) { invert_m4(bpt->rotscale_mat); invert_m4(bpt->loc_mat); - invert_v3(bpt->post_scale); + invert_v3_safe(bpt->post_scale); } void BKE_bone_parent_transform_combine(const struct BoneParentTransform *in1, @@ -2663,7 +2662,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_ } } - /* printf("rebuild pose %s, %d bones\n", ob->id.name, counter); */ + // printf("rebuild pose %s, %d bones\n", ob->id.name, counter); /* synchronize protected layers with proxy */ /* HACK! To preserve 2.7x behavior that you always can pose even locked bones, diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 47853deec3e..99eb064d061 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -180,7 +180,7 @@ class BKE_armature_find_selected_bones_test : public testing::Test { BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent - // Make sure the armature & its bones are visible, to make them selectable. + /* Make sure the armature & its bones are visible, to make them selectable. */ arm.layer = bone1.layer = bone2.layer = bone3.layer = 1; } }; @@ -200,8 +200,8 @@ TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected) EXPECT_EQ(seen_bones[0], &bone1); EXPECT_EQ(seen_bones[1], &bone3); - EXPECT_FALSE(result.all_bones_selected); // Bone 2 was not selected. - EXPECT_FALSE(result.no_bones_selected); // Bones 1 and 3 were selected. + EXPECT_FALSE(result.all_bones_selected); /* Bone 2 was not selected. */ + EXPECT_FALSE(result.no_bones_selected); /* Bones 1 and 3 were selected. */ } TEST_F(BKE_armature_find_selected_bones_test, no_bones_selected) diff --git a/source/blender/blenkernel/intern/autoexec.c b/source/blender/blenkernel/intern/autoexec.c index 07b096af941..3aec646b024 100644 --- a/source/blender/blenkernel/intern/autoexec.c +++ b/source/blender/blenkernel/intern/autoexec.c @@ -56,7 +56,7 @@ bool BKE_autoexec_match(const char *path) if (path_cmp->path[0] == '\0') { /* pass */ } - else if ((path_cmp->flag & USER_PATHCMP_GLOB)) { + else if (path_cmp->flag & USER_PATHCMP_GLOB) { if (fnmatch(path_cmp->path, path, fnmatch_flags) == 0) { return true; } diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 61827be08e5..1c5d8804280 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -660,10 +660,6 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) BKE_studiolight_default(userdef->light_param, userdef->light_ambient); BKE_preferences_asset_library_default_add(userdef); - /* Enable asset browser features by default for alpha testing. - * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha - * builds. */ - userdef->experimental.use_asset_browser = true; return userdef; } diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 9caf416cd0c..a7257133821 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -342,10 +342,7 @@ static bool rule_avoid_collision(BoidRule *rule, } } } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* check boids in other systems */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -401,10 +398,7 @@ static bool rule_avoid_collision(BoidRule *rule, } } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } @@ -435,10 +429,7 @@ static bool rule_separate(BoidRule *UNUSED(rule), len = ptn[1].dist; ret = 1; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* check other boid systems */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -457,10 +448,7 @@ static bool rule_separate(BoidRule *UNUSED(rule), ret = true; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } return ret; @@ -723,10 +711,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part f_strength += bbd->part->boids->strength * health; - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); /* add other friendlies and calculate enemy strength and find closest enemy */ for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) { @@ -755,10 +740,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part f_strength += epsys->part->boids->strength * health; } - if (ptn) { - MEM_freeN(ptn); - ptn = NULL; - } + MEM_SAFE_FREE(ptn); } } /* decide action if enemy presence found */ diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index abf7bab7612..7b81187be21 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -202,49 +202,48 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Brush *brush = (Brush *)id; - if (brush->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, Brush, id_address, &brush->id); - BKE_id_blend_write(writer, &brush->id); - if (brush->curve) { - BKE_curvemapping_blend_write(writer, brush->curve); - } + BLO_write_id_struct(writer, Brush, id_address, &brush->id); + BKE_id_blend_write(writer, &brush->id); - if (brush->gpencil_settings) { - BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings); + if (brush->curve) { + BKE_curvemapping_blend_write(writer, brush->curve); + } - if (brush->gpencil_settings->curve_sensitivity) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity); - } - if (brush->gpencil_settings->curve_strength) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength); - } - if (brush->gpencil_settings->curve_jitter) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter); - } - if (brush->gpencil_settings->curve_rand_pressure) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure); - } - if (brush->gpencil_settings->curve_rand_strength) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength); - } - if (brush->gpencil_settings->curve_rand_uv) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv); - } - if (brush->gpencil_settings->curve_rand_hue) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue); - } - if (brush->gpencil_settings->curve_rand_saturation) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation); - } - if (brush->gpencil_settings->curve_rand_value) { - BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value); - } + if (brush->gpencil_settings) { + BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings); + + if (brush->gpencil_settings->curve_sensitivity) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity); + } + if (brush->gpencil_settings->curve_strength) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength); + } + if (brush->gpencil_settings->curve_jitter) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter); + } + if (brush->gpencil_settings->curve_rand_pressure) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure); + } + if (brush->gpencil_settings->curve_rand_strength) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength); + } + if (brush->gpencil_settings->curve_rand_uv) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv); + } + if (brush->gpencil_settings->curve_rand_hue) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue); + } + if (brush->gpencil_settings->curve_rand_saturation) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation); } - if (brush->gradient) { - BLO_write_struct(writer, ColorBand, brush->gradient); + if (brush->gpencil_settings->curve_rand_value) { + BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value); } } + if (brush->gradient) { + BLO_write_struct(writer, ColorBand, brush->gradient); + } } static void brush_blend_read_data(BlendDataReader *reader, ID *id) @@ -660,10 +659,7 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) break; } - if (cuma->table) { - MEM_freeN(cuma->table); - cuma->table = NULL; - } + MEM_SAFE_FREE(cuma->table); } void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index eaba5d33a20..87b1584d422 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -49,12 +49,18 @@ #include "DEG_depsgraph_query.h" +#include "RE_engine.h" + #include "BLO_read_write.h" #ifdef WITH_ALEMBIC # include "ABC_alembic.h" #endif +#ifdef WITH_USD +# include "usd.h" +#endif + static void cachefile_handle_free(CacheFile *cache_file); static void cache_file_init_data(ID *id) @@ -91,19 +97,18 @@ static void cache_file_free_data(ID *id) static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_address) { CacheFile *cache_file = (CacheFile *)id; - if (cache_file->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&cache_file->object_paths); - cache_file->handle = NULL; - memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath)); - cache_file->handle_readers = NULL; - BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); - BKE_id_blend_write(writer, &cache_file->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&cache_file->object_paths); + cache_file->handle = NULL; + memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath)); + cache_file->handle_readers = NULL; + + BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); + BKE_id_blend_write(writer, &cache_file->id); - if (cache_file->adt) { - BKE_animdata_blend_write(writer, cache_file->adt); - } + if (cache_file->adt) { + BKE_animdata_blend_write(writer, cache_file->adt); } } @@ -166,15 +171,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file, Object *object, const char *object_path) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) + BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE); if (cache_file->handle == NULL) { return; } - /* Open Alembic cache reader. */ - *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + /* Open Alembic cache reader. */ + *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + /* Open USD cache reader. */ + *reader = CacheReader_open_usd_object(cache_file->handle, *reader, object, object_path); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } /* Multiple modifiers and constraints can call this function concurrently. */ BLI_spin_lock(&spin); @@ -197,16 +217,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file, void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reader) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) /* Multiple modifiers and constraints can call this function concurrently, and * cachefile_handle_free() can also be called at the same time. */ BLI_spin_lock(&spin); if (*reader != NULL) { if (cache_file) { BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE); + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_CacheReader_free(*reader); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_CacheReader_free(*reader); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } } - CacheReader_free(*reader); *reader = NULL; if (cache_file && cache_file->handle_readers) { @@ -221,7 +255,8 @@ void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reade static void cachefile_handle_free(CacheFile *cache_file) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) + /* Free readers in all modifiers and constraints that use the handle, before * we free the handle itself. */ BLI_spin_lock(&spin); @@ -230,7 +265,21 @@ static void cachefile_handle_free(CacheFile *cache_file) GSET_ITER (gs_iter, cache_file->handle_readers) { struct CacheReader **reader = BLI_gsetIterator_getKey(&gs_iter); if (*reader != NULL) { - CacheReader_free(*reader); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_CacheReader_free(*reader); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_CacheReader_free(*reader); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } + *reader = NULL; } } @@ -242,7 +291,22 @@ static void cachefile_handle_free(CacheFile *cache_file) /* Free handle. */ if (cache_file->handle) { - ABC_free_handle(cache_file->handle); + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_free_handle(cache_file->handle); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } + cache_file->handle = NULL; } @@ -289,12 +353,22 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file BLI_freelistN(&cache_file->object_paths); #ifdef WITH_ALEMBIC - cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths); - BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + if (BLI_path_extension_check_glob(filepath, "*abc")) { + cache_file->type = CACHEFILE_TYPE_ALEMBIC; + cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths); + BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + } +#endif +#ifdef WITH_USD + if (BLI_path_extension_check_glob(filepath, "*.usd;*.usda;*.usdc")) { + cache_file->type = CACHEFILE_TYPE_USD; + cache_file->handle = USD_create_handle(bmain, filepath, &cache_file->object_paths); + BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX); + } #endif if (DEG_is_active(depsgraph)) { - /* Flush object paths back to original datablock for UI. */ + /* Flush object paths back to original data-block for UI. */ CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id); BLI_freelistN(&cache_file_orig->object_paths); BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths); @@ -336,3 +410,25 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c const float frame = (cache_file->override_frame ? cache_file->frame : time); return cache_file->is_sequence ? frame : frame / fps - time_offset; } + +/** + * Determine whether the #CacheFile should use a render engine procedural. If so, data is not read + * from the file and bounding boxes are used to represent the objects in the Scene. + * Render engines will receive the bounding box as a placeholder but can instead + * load the data directly if they support it. + */ +bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, + Scene *scene, + const int dag_eval_mode) +{ + RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); + + if (cache_file->type != CACHEFILE_TYPE_ALEMBIC || + !RE_engine_supports_alembic_procedural(render_engine_type, scene)) { + return false; + } + + /* The render time procedural is only enabled during viewport rendering. */ + const bool is_final_render = (eEvaluationMode)dag_eval_mode == DAG_EVAL_RENDER; + return cache_file->use_render_procedural && !is_final_render; +} diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 5172b067eba..46b079fb42e 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -122,18 +122,17 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data) static void camera_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Camera *cam = (Camera *)id; - if (cam->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Camera, id_address, &cam->id); - BKE_id_blend_write(writer, &cam->id); - if (cam->adt) { - BKE_animdata_blend_write(writer, cam->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Camera, id_address, &cam->id); + BKE_id_blend_write(writer, &cam->id); - LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { - BLO_write_struct(writer, CameraBGImage, bgpic); - } + if (cam->adt) { + BKE_animdata_blend_write(writer, cam->adt); + } + + LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { + BLO_write_struct(writer, CameraBGImage, bgpic); } } diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 0fa58a74f2b..080a7c90c46 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -42,6 +42,7 @@ #include "BKE_cloth.h" #include "BKE_effect.h" #include "BKE_global.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" @@ -447,11 +448,7 @@ void cloth_free_modifier(ClothModifierData *clmd) SIM_cloth_solver_free(clmd); /* Free the verts. */ - if (cloth->verts != NULL) { - MEM_freeN(cloth->verts); - } - - cloth->verts = NULL; + MEM_SAFE_FREE(cloth->verts); cloth->mvert_num = 0; /* Free the springs. */ @@ -529,11 +526,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd) SIM_cloth_solver_free(clmd); /* Free the verts. */ - if (cloth->verts != NULL) { - MEM_freeN(cloth->verts); - } - - cloth->verts = NULL; + MEM_SAFE_FREE(cloth->verts); cloth->mvert_num = 0; /* Free the springs. */ @@ -1582,7 +1575,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free(tmp_mesh); + BKE_id_free(NULL, &tmp_mesh->id); } return false; } @@ -1591,7 +1584,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) BLI_edgeset_free(existing_vert_pairs); free_bvhtree_from_mesh(&treedata); if (tmp_mesh) { - BKE_mesh_free(tmp_mesh); + BKE_id_free(NULL, &tmp_mesh->id); } BLI_rng_free(rng); } diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index b90214f1814..d36e9b67d00 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -214,20 +214,19 @@ void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collectio static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Collection *collection = (Collection *)id; - if (collection->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; - collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED; - collection->tag = 0; - BLI_listbase_clear(&collection->object_cache); - BLI_listbase_clear(&collection->object_cache_instanced); - BLI_listbase_clear(&collection->parents); - /* write LibData */ - BLO_write_id_struct(writer, Collection, id_address, &collection->id); + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED; + collection->tag = 0; + BLI_listbase_clear(&collection->object_cache); + BLI_listbase_clear(&collection->object_cache_instanced); + BLI_listbase_clear(&collection->parents); - BKE_collection_blend_write_nolib(writer, collection); - } + /* write LibData */ + BLO_write_id_struct(writer, Collection, id_address, &collection->id); + + BKE_collection_blend_write_nolib(writer, collection); } #ifdef USE_COLLECTION_COMPAT_28 @@ -508,7 +507,7 @@ void BKE_collection_add_from_collection(Main *bmain, * \{ */ /** Free (or release) any data used by this collection (does not free the collection itself). */ -void BKE_collection_free(Collection *collection) +void BKE_collection_free_data(Collection *collection) { BKE_libblock_free_data(&collection->id, false); collection_free_data(&collection->id); @@ -806,10 +805,10 @@ static void collection_object_cache_fill(ListBase *lb, /* Only collection flags are checked here currently, object restrict flag is checked * in FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN since it can be animated * without updating the cache. */ - if (((child_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0)) { + if (((child_restrict & COLLECTION_HIDE_VIEWPORT) == 0)) { base->flag |= BASE_ENABLED_VIEWPORT; } - if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0)) { + if (((child_restrict & COLLECTION_HIDE_RENDER) == 0)) { base->flag |= BASE_ENABLED_RENDER; } } @@ -1755,7 +1754,7 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect { bool changed = false; - if (collection->flag & COLLECTION_RESTRICT_SELECT) { + if (collection->flag & COLLECTION_HIDE_SELECT) { return false; } diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index a9f0f69b855..f2c2e552a9f 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1716,22 +1716,10 @@ void BKE_scopes_update(Scopes *scopes, void BKE_scopes_free(Scopes *scopes) { - if (scopes->waveform_1) { - MEM_freeN(scopes->waveform_1); - scopes->waveform_1 = NULL; - } - if (scopes->waveform_2) { - MEM_freeN(scopes->waveform_2); - scopes->waveform_2 = NULL; - } - if (scopes->waveform_3) { - MEM_freeN(scopes->waveform_3); - scopes->waveform_3 = NULL; - } - if (scopes->vecscope) { - MEM_freeN(scopes->vecscope); - scopes->vecscope = NULL; - } + MEM_SAFE_FREE(scopes->waveform_1); + MEM_SAFE_FREE(scopes->waveform_2); + MEM_SAFE_FREE(scopes->waveform_3); + MEM_SAFE_FREE(scopes->vecscope); } void BKE_scopes_new(Scopes *scopes) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 47df31e3a2c..30aa22387d0 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -95,6 +95,10 @@ # include "ABC_alembic.h" #endif +#ifdef WITH_USD +# include "usd.h" +#endif + /* ---------------------------------------------------------------------------- */ /* Useful macros for testing various common flag combinations */ @@ -2978,6 +2982,16 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ if (VALID_CONS_TARGET(ct) || data->flag & ACTCON_USE_EVAL_TIME) { switch (data->mix_mode) { + /* Simple matrix multiplication. */ + case ACTCON_MIX_BEFORE_FULL: + mul_m4_m4m4(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_FULL: + mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + break; + + /* Aligned Inherit Scale emulation. */ case ACTCON_MIX_BEFORE: mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); break; @@ -2986,8 +3000,13 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); break; - case ACTCON_MIX_AFTER_FULL: - mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + /* Fully separate handling of channels. */ + case ACTCON_MIX_BEFORE_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, ct->matrix); break; default: @@ -4612,9 +4631,7 @@ static void splineik_free(bConstraint *con) bSplineIKConstraint *data = con->data; /* binding array */ - if (data->points) { - MEM_freeN(data->points); - } + MEM_SAFE_FREE(data->points); } static void splineik_copy(bConstraint *con, bConstraint *srccon) @@ -5403,7 +5420,7 @@ static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, vo static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) { -#ifdef WITH_ALEMBIC +#if defined(WITH_ALEMBIC) || defined(WITH_USD) bTransformCacheConstraint *data = con->data; Scene *scene = cob->scene; @@ -5413,6 +5430,11 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa return; } + /* Do not process data if using a render time procedural. */ + if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(cob->depsgraph))) { + return; + } + const float frame = DEG_get_ctime(cob->depsgraph); const float time = BKE_cachefile_time_offset(cache_file, frame, FPS); @@ -5421,7 +5443,20 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa BKE_cachefile_reader_open(cache_file, &data->reader, cob->ob, data->object_path); } - ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale); + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + USD_get_transform(data->reader, cob->matrix, time * FPS, cache_file->scale); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } #else UNUSED_VARS(con, cob); #endif @@ -5647,6 +5682,111 @@ bool BKE_constraint_remove_ex(ListBase *list, Object *ob, bConstraint *con, bool return false; } +/* Apply the specified constraint in the given constraint stack */ +bool BKE_constraint_apply_for_object(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + bConstraint *con) +{ + if (!con) { + return false; + } + + const float ctime = BKE_scene_frame_get(scene); + + bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob)); + ListBase single_con = {new_con, new_con}; + + bConstraintOb *cob = BKE_constraints_make_evalob( + depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT); + /* Undo the effect of the current constraint stack evaluation. */ + mul_m4_m4m4(cob->matrix, ob->constinv, cob->matrix); + + /* Evaluate single constraint. */ + BKE_constraints_solve(depsgraph, &single_con, cob, ctime); + /* Copy transforms back. This will leave the object in a bad state + * as ob->constinv will be wrong until next evaluation. */ + BKE_constraints_clear_evalob(cob); + + /* Free the copied constraint. */ + BKE_constraint_free_data(new_con); + BLI_freelinkN(&single_con, new_con); + + /* Apply transform from matrix. */ + BKE_object_apply_mat4(ob, ob->obmat, true, true); + + return true; +} + +bool BKE_constraint_apply_and_remove_for_object(Depsgraph *depsgraph, + Scene *scene, + ListBase /*bConstraint*/ *constraints, + Object *ob, + bConstraint *con) +{ + if (!BKE_constraint_apply_for_object(depsgraph, scene, ob, con)) { + return false; + } + + return BKE_constraint_remove_ex(constraints, ob, con, true); +} + +bool BKE_constraint_apply_for_pose( + Depsgraph *depsgraph, Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con) +{ + if (!con) { + return false; + } + + const float ctime = BKE_scene_frame_get(scene); + + bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob)); + ListBase single_con; + single_con.first = new_con; + single_con.last = new_con; + + float vec[3]; + copy_v3_v3(vec, pchan->pose_mat[3]); + + bConstraintOb *cob = BKE_constraints_make_evalob( + depsgraph, scene, ob, pchan, CONSTRAINT_OBTYPE_BONE); + /* Undo the effects of currently applied constraints. */ + mul_m4_m4m4(cob->matrix, pchan->constinv, cob->matrix); + /* Evaluate single constraint. */ + BKE_constraints_solve(depsgraph, &single_con, cob, ctime); + BKE_constraints_clear_evalob(cob); + + /* Free the copied constraint. */ + BKE_constraint_free_data(new_con); + BLI_freelinkN(&single_con, new_con); + + /* Prevent constraints breaking a chain. */ + if (pchan->bone->flag & BONE_CONNECTED) { + copy_v3_v3(pchan->pose_mat[3], vec); + } + + /* Apply transform from matrix. */ + float mat[4][4]; + BKE_armature_mat_pose_to_bone(pchan, pchan->pose_mat, mat); + BKE_pchan_apply_mat4(pchan, mat, true); + + return true; +} + +bool BKE_constraint_apply_and_remove_for_pose(Depsgraph *depsgraph, + Scene *scene, + ListBase /*bConstraint*/ *constraints, + Object *ob, + bConstraint *con, + bPoseChannel *pchan) +{ + if (!BKE_constraint_apply_for_pose(depsgraph, scene, ob, pchan, con)) { + return false; + } + + return BKE_constraint_remove_ex(constraints, ob, con, true); +} + void BKE_constraint_panel_expand(bConstraint *con) { con->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT; @@ -5752,6 +5892,17 @@ static bConstraint *add_new_constraint(Object *ob, } break; } + case CONSTRAINT_TYPE_ACTION: { + /* The Before or Split modes require computing in local space, but + * for objects the Local space doesn't make sense (T78462, D6095 etc). + * So only default to Before (Split) if the constraint is on a bone. */ + if (pchan) { + bActionConstraint *data = con->data; + data->mix_mode = ACTCON_MIX_BEFORE_SPLIT; + con->ownspace = CONSTRAINT_SPACE_LOCAL; + } + break; + } } return con; diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index dced945bea0..7763bb9ca08 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1448,9 +1448,9 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list) return ctx_data_collection_get(C, "editable_gpencil_strokes", list); } -const AssetLibraryReference *CTX_wm_asset_library(const bContext *C) +const AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C) { - return ctx_data_pointer_get(C, "asset_library"); + return ctx_data_pointer_get(C, "asset_library_ref"); } AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index f1369254347..397838e6904 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -146,51 +146,50 @@ static void curve_foreach_id(ID *id, LibraryForeachIDData *data) static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Curve *cu = (Curve *)id; - if (cu->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - cu->editnurb = NULL; - cu->editfont = NULL; - cu->batch_cache = NULL; - /* write LibData */ - BLO_write_id_struct(writer, Curve, id_address, &cu->id); - BKE_id_blend_write(writer, &cu->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + cu->editnurb = NULL; + cu->editfont = NULL; + cu->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, Curve, id_address, &cu->id); + BKE_id_blend_write(writer, &cu->id); - /* direct data */ - BLO_write_pointer_array(writer, cu->totcol, cu->mat); - if (cu->adt) { - BKE_animdata_blend_write(writer, cu->adt); - } + /* direct data */ + BLO_write_pointer_array(writer, cu->totcol, cu->mat); + if (cu->adt) { + BKE_animdata_blend_write(writer, cu->adt); + } - if (cu->vfont) { - BLO_write_raw(writer, cu->len + 1, cu->str); - BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); - BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); + if (cu->vfont) { + BLO_write_raw(writer, cu->len + 1, cu->str); + BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo); + BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb); + } + else { + /* is also the order of reading */ + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { + BLO_write_struct(writer, Nurb, nu); } - else { - /* is also the order of reading */ - LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { - BLO_write_struct(writer, Nurb, nu); + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { + if (nu->type == CU_BEZIER) { + BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt); } - LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { - if (nu->type == CU_BEZIER) { - BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt); + else { + BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp); + if (nu->knotsu) { + BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu); } - else { - BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp); - if (nu->knotsu) { - BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu); - } - if (nu->knotsv) { - BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv); - } + if (nu->knotsv) { + BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv); } } } + } - if (cu->bevel_profile != NULL) { - BKE_curveprofile_blend_write(writer, cu->bevel_profile); - } + if (cu->bevel_profile != NULL) { + BKE_curveprofile_blend_write(writer, cu->bevel_profile); } } @@ -638,7 +637,7 @@ void BKE_nurb_free(Nurb *nu) MEM_freeN(nu->knotsv); } nu->knotsv = NULL; - /* if (nu->trim.first) freeNurblist(&(nu->trim)); */ + // if (nu->trim.first) freeNurblist(&(nu->trim)); MEM_freeN(nu); } @@ -2331,17 +2330,21 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl) bevp1 = bevp2 + (bl->nr - 1); bevp0 = bevp1 - 1; - nr = bl->nr; - while (nr--) { + /* The ordinal of the point being adjusted (bevp2). First point is 1. */ - if (nr + 3 > bl->nr) { /* first time and second time, otherwise first point adjusts last */ - vec_to_quat(bevp1->quat, bevp1->dir, 5, 1); - } - else { - minimum_twist_between_two_points(bevp1, bevp0); - } + /* First point is the reference, don't adjust. + * Skip this point in the following loop. */ + if (bl->nr > 0) { + vec_to_quat(bevp2->quat, bevp2->dir, 5, 1); - bevp0 = bevp1; + bevp0 = bevp1; /* bevp0 is unused */ + bevp1 = bevp2; + bevp2++; + } + for (nr = 1; nr < bl->nr; nr++) { + minimum_twist_between_two_points(bevp2, bevp1); + + bevp0 = bevp1; /* bevp0 is unused */ bevp1 = bevp2; bevp2++; } @@ -5010,10 +5013,7 @@ bool BKE_nurb_type_convert(Nurb *nu, MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */ } nu->knotsu = NULL; - if (nu->knotsv) { - MEM_freeN(nu->knotsv); - } - nu->knotsv = NULL; + MEM_SAFE_FREE(nu->knotsv); } else if (type == CU_BEZIER) { /* to Bezier */ nr = nu->pntsu / 3; diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 7aa9d1958eb..1a3200a9b6c 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -724,10 +724,7 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) GridPaintMask *gpm = data; for (int i = 0; i < count; i++) { - if (gpm[i].data) { - MEM_freeN(gpm[i].data); - } - gpm[i].data = NULL; + MEM_SAFE_FREE(gpm[i].data); gpm[i].level = 0; } } @@ -4249,7 +4246,7 @@ void CustomData_blend_write_prepare(CustomData *data, CustomDataLayer *layer = &data->layers[i]; if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */ data->totlayer--; - /* CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); */ + // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); } else { if (UNLIKELY((size_t)j >= write_layers_size)) { diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 605061570b8..b83621e8b79 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -301,14 +301,12 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, } if (dirty_nors_dst || do_poly_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, num_verts_dst, loops_dst, - polys_dst, num_loops_dst, + polys_dst, num_polys_dst, - poly_nors_dst, - true); + poly_nors_dst); } /* Cache loop nors into a temp CDLayer. */ loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL); diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index f7ef84728b6..13222747a52 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -894,10 +894,7 @@ void BKE_defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw) void BKE_defvert_clear(MDeformVert *dvert) { - if (dvert->dw) { - MEM_freeN(dvert->dw); - dvert->dw = NULL; - } + MEM_SAFE_FREE(dvert->dw); dvert->totweight = 0; } diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 99dc1db9d38..c97e07ad487 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -1003,7 +1003,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, modified = temp_mesh; BKE_mesh_vert_coords_apply(modified, vertCos); - BKE_mesh_calc_normals_mapping_simple(modified); + BKE_mesh_calc_normals(modified); MEM_freeN(vertCos); } diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 52996e3bcc7..8f94c407cae 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -844,10 +844,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface) if (temp_s_num) { MEM_freeN(temp_s_num); } - if (temp_t_index) { - MEM_freeN(temp_t_index); - } - grid->temp_t_index = NULL; + MEM_SAFE_FREE(temp_t_index); if (error || !grid->s_num) { setError(surface->canvas, N_("Not enough free memory")); @@ -988,10 +985,7 @@ void dynamicPaint_freeSurface(const DynamicPaintModifierData *pmd, DynamicPaintS } surface->pointcache = NULL; - if (surface->effector_weights) { - MEM_freeN(surface->effector_weights); - } - surface->effector_weights = NULL; + MEM_SAFE_FREE(surface->effector_weights); BLI_remlink(&(surface->canvas->surfaces), surface); dynamicPaint_freeSurfaceData(surface); diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index fc1721eaf3a..334118ddf3f 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -861,7 +861,7 @@ static void get_effector_tot( int totpart = eff->psys->totpart; int amount = eff->psys->part->effector_amount; - *step = (totpart > amount) ? totpart / amount : 1; + *step = (totpart > amount) ? (int)ceil((float)totpart / (float)amount) : 1; } } else { diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 92fd220549a..799d6553682 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -4153,7 +4153,7 @@ static void BKE_fluid_modifier_process( { const int scene_framenr = (int)DEG_get_ctime(depsgraph); - if ((fmd->type & MOD_FLUID_TYPE_FLOW)) { + if (fmd->type & MOD_FLUID_TYPE_FLOW) { BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr); } else if (fmd->type & MOD_FLUID_TYPE_EFFEC) { @@ -4766,20 +4766,14 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) BLI_rw_mutex_free(fmd->domain->fluid_mutex); } - if (fmd->domain->effector_weights) { - MEM_freeN(fmd->domain->effector_weights); - } - fmd->domain->effector_weights = NULL; + MEM_SAFE_FREE(fmd->domain->effector_weights); if (!(fmd->modifier.flag & eModifierFlag_SharedCaches)) { BKE_ptcache_free_list(&(fmd->domain->ptcaches[0])); fmd->domain->point_cache[0] = NULL; } - if (fmd->domain->mesh_velocities) { - MEM_freeN(fmd->domain->mesh_velocities); - } - fmd->domain->mesh_velocities = NULL; + MEM_SAFE_FREE(fmd->domain->mesh_velocities); if (fmd->domain->coba) { MEM_freeN(fmd->domain->coba); @@ -4798,10 +4792,7 @@ static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd) } fmd->flow->mesh = NULL; - if (fmd->flow->verts_old) { - MEM_freeN(fmd->flow->verts_old); - } - fmd->flow->verts_old = NULL; + MEM_SAFE_FREE(fmd->flow->verts_old); fmd->flow->numverts = 0; fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; @@ -4818,10 +4809,7 @@ static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd) } fmd->effector->mesh = NULL; - if (fmd->effector->verts_old) { - MEM_freeN(fmd->effector->verts_old); - } - fmd->effector->verts_old = NULL; + MEM_SAFE_FREE(fmd->effector->verts_old); fmd->effector->numverts = 0; fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; @@ -4857,18 +4845,12 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need fmd->domain->active_fields = 0; } else if (fmd->flow) { - if (fmd->flow->verts_old) { - MEM_freeN(fmd->flow->verts_old); - } - fmd->flow->verts_old = NULL; + MEM_SAFE_FREE(fmd->flow->verts_old); fmd->flow->numverts = 0; fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE; } else if (fmd->effector) { - if (fmd->effector->verts_old) { - MEM_freeN(fmd->effector->verts_old); - } - fmd->effector->verts_old = NULL; + MEM_SAFE_FREE(fmd->effector->verts_old); fmd->effector->numverts = 0; fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE; } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index d0b9aeefa55..c1765967238 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -126,23 +126,22 @@ static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_addres { VFont *vf = (VFont *)id; const bool is_undo = BLO_write_is_undo(writer); - if (vf->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - vf->data = NULL; - vf->temp_pf = NULL; - - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) { - vf->packedfile = NULL; - } - /* write LibData */ - BLO_write_id_struct(writer, VFont, id_address, &vf->id); - BKE_id_blend_write(writer, &vf->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + vf->data = NULL; + vf->temp_pf = NULL; - /* direct data */ - BKE_packedfile_blend_write(writer, vf->packedfile); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) { + vf->packedfile = NULL; } + + /* write LibData */ + BLO_write_id_struct(writer, VFont, id_address, &vf->id); + BKE_id_blend_write(writer, &vf->id); + + /* direct data */ + BKE_packedfile_blend_write(writer, vf->packedfile); } static void vfont_blend_read_data(BlendDataReader *reader, ID *id) @@ -337,13 +336,9 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) vfd = BLI_vfontdata_from_freetypefont(pf); if (vfd) { - vfont = BKE_libblock_alloc(bmain, ID_VF, filename, 0); + /* If there's a font name, use it for the ID name. */ + vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0); vfont->data = vfd; - - /* if there's a font name, use it for the ID name */ - if (vfd->name[0] != '\0') { - BLI_strncpy(vfont->id.name + 2, vfd->name, sizeof(vfont->id.name) - 2); - } BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath)); /* if autopack is on store the packedfile in de font structure */ @@ -719,6 +714,13 @@ typedef struct VFontToCurveIter { float max; } bisect; bool ok; + /** + * Wrap words that extends beyond the text-box width (enabled by default). + * + * Currently only disabled when scale-to-fit is enabled, + * so floating-point error doesn't cause unexpected wrapping, see T89241. + */ + bool word_wrap; int status; } VFontToCurveIter; @@ -781,6 +783,7 @@ static bool vfont_to_curve(Object *ob, char32_t ascii; bool ok = false; const float font_size = cu->fsize * iter_data->scale_to_fit; + const bool word_wrap = iter_data->word_wrap; const float xof_scale = cu->xof / font_size; const float yof_scale = cu->yof / font_size; int last_line = -1; @@ -951,43 +954,59 @@ static bool vfont_to_curve(Object *ob, twidth = char_width(cu, che, info); - /* Calculate positions */ - if ((tb_scale.w != 0.0f) && (ct->dobreak == 0) && - (((xof - tb_scale.x) + twidth) > xof_scale + tb_scale.w)) { - // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]); - for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { - bool dobreak = false; - if (ELEM(mem[j], ' ', '-')) { - ct -= (i - (j - 1)); - cnr -= (i - (j - 1)); - if (mem[j] == ' ') { - wsnr--; + /* Calculate positions. */ + + if ((tb_scale.w != 0.0f) && (ct->dobreak == 0)) { /* May need wrapping. */ + const float x_available = xof_scale + tb_scale.w; + const float x_used = (xof - tb_scale.x) + twidth; + + if (word_wrap == false) { + /* When scale to fit is used, don't do any wrapping. + * + * Floating precision error can cause the text to be slightly larger. + * Assert this is a small value as large values indicate incorrect + * calculations with scale-to-fit which shouldn't be ignored. See T89241. */ + if (x_used > x_available) { + BLI_assert_msg(compare_ff_relative(x_used, x_available, FLT_EPSILON, 64), + "VFontToCurveIter.scale_to_fit not set correctly!"); + } + } + else if (x_used > x_available) { + // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]); + for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { + bool dobreak = false; + if (ELEM(mem[j], ' ', '-')) { + ct -= (i - (j - 1)); + cnr -= (i - (j - 1)); + if (mem[j] == ' ') { + wsnr--; + } + if (mem[j] == '-') { + wsnr++; + } + i = j - 1; + xof = ct->xof; + ct[1].dobreak = 1; + custrinfo[i + 1].flag |= CU_CHINFO_WRAP; + dobreak = true; } - if (mem[j] == '-') { - wsnr++; + else if (chartransdata[j].dobreak) { + // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]); + ct->dobreak = 1; + custrinfo[i + 1].flag |= CU_CHINFO_WRAP; + ct -= 1; + cnr -= 1; + i--; + xof = ct->xof; + dobreak = true; } - i = j - 1; - xof = ct->xof; - ct[1].dobreak = 1; - custrinfo[i + 1].flag |= CU_CHINFO_WRAP; - dobreak = true; - } - else if (chartransdata[j].dobreak) { - // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]); - ct->dobreak = 1; - custrinfo[i + 1].flag |= CU_CHINFO_WRAP; - ct -= 1; - cnr -= 1; - i--; - xof = ct->xof; - dobreak = true; - } - if (dobreak) { - if (tb_scale.h == 0.0f) { - /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ - custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; + if (dobreak) { + if (tb_scale.h == 0.0f) { + /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ + custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; + } + goto makebreak; } - goto makebreak; } } } @@ -1549,6 +1568,7 @@ static bool vfont_to_curve(Object *ob, const float total_text_height = lnr * linedist; iter_data->scale_to_fit = tb_scale.h / total_text_height; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } else if (tb_scale.h == 0.0f) { @@ -1556,10 +1576,10 @@ static bool vfont_to_curve(Object *ob, if (longest_line_length > tb_scale.w) { /* We make sure longest line before it broke can fit here. */ float scale_to_fit = tb_scale.w / longest_line_length; - scale_to_fit -= FLT_EPSILON; iter_data->scale_to_fit = scale_to_fit; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } } @@ -1620,6 +1640,7 @@ static bool vfont_to_curve(Object *ob, else { iter_data->scale_to_fit = iter_data->bisect.min; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; + iter_data->word_wrap = false; } } } @@ -1689,6 +1710,7 @@ bool BKE_vfont_to_curve_ex(Object *ob, VFontToCurveIter data = { .iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS, .scale_to_fit = 1.0f, + .word_wrap = true, .ok = true, .status = VFONT_TO_CURVE_INIT, }; diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 90a97264c8f..32a65ab47bf 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -491,6 +491,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou } } + /* A possible optimization is to only tag the normals dirty when there are transforms that change + * normals. */ + BKE_mesh_normals_tag_dirty(new_mesh); + return new_mesh; } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 38397f8f307..9062fd2d39c 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -131,7 +131,7 @@ static void greasepencil_free_data(ID *id) { /* Really not ideal, but for now will do... In theory custom behaviors like not freeing cache * should be handled through specific API, and not be part of the generic one. */ - BKE_gpencil_free((bGPdata *)id, true); + BKE_gpencil_free_data((bGPdata *)id, true); } static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) @@ -150,47 +150,46 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bGPdata *gpd = (bGPdata *)id; - if (gpd->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - /* XXX not sure why the whole run-time data is not cleared in reading code, - * for now mimicking it here. */ - gpd->runtime.sbuffer = NULL; - gpd->runtime.sbuffer_used = 0; - gpd->runtime.sbuffer_size = 0; - gpd->runtime.tot_cp_points = 0; - /* write gpd data block to file */ - BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); - BKE_id_blend_write(writer, &gpd->id); + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + /* XXX not sure why the whole run-time data is not cleared in reading code, + * for now mimicking it here. */ + gpd->runtime.sbuffer = NULL; + gpd->runtime.sbuffer_used = 0; + gpd->runtime.sbuffer_size = 0; + gpd->runtime.tot_cp_points = 0; - if (gpd->adt) { - BKE_animdata_blend_write(writer, gpd->adt); - } + /* write gpd data block to file */ + BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); + BKE_id_blend_write(writer, &gpd->id); - BKE_defbase_blend_write(writer, &gpd->vertex_group_names); + if (gpd->adt) { + BKE_animdata_blend_write(writer, gpd->adt); + } - BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); + BKE_defbase_blend_write(writer, &gpd->vertex_group_names); - /* write grease-pencil layers to file */ - BLO_write_struct_list(writer, bGPDlayer, &gpd->layers); - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - /* Write mask list. */ - BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers); - /* write this layer's frames to file */ - BLO_write_struct_list(writer, bGPDframe, &gpl->frames); - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - /* write strokes */ - BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes); - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); - BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); - BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); - if (gps->editcurve != NULL) { - bGPDcurve *gpc = gps->editcurve; - BLO_write_struct(writer, bGPDcurve, gpc); - BLO_write_struct_array( - writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points); - } + BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); + + /* write grease-pencil layers to file */ + BLO_write_struct_list(writer, bGPDlayer, &gpd->layers); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Write mask list. */ + BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers); + /* write this layer's frames to file */ + BLO_write_struct_list(writer, bGPDframe, &gpl->frames); + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* write strokes */ + BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes); + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); + BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); + BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); + if (gps->editcurve != NULL) { + bGPDcurve *gpc = gps->editcurve; + BLO_write_struct(writer, bGPDcurve, gpc); + BLO_write_struct_array( + writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points); } } } @@ -496,7 +495,7 @@ void BKE_gpencil_free_layers(ListBase *list) } /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd, bool free_all) +void BKE_gpencil_free_data(bGPdata *gpd, bool free_all) { /* free layers */ BKE_gpencil_free_layers(&gpd->layers); @@ -519,8 +518,9 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all) */ void BKE_gpencil_eval_delete(bGPdata *gpd_eval) { - BKE_gpencil_free(gpd_eval, true); + BKE_gpencil_free_data(gpd_eval, true); BKE_libblock_free_data(&gpd_eval->id, false); + BLI_assert(!gpd_eval->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(gpd_eval); } diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index f8a07939096..0f218d6166c 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -31,6 +31,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array_utils.h" #include "BLI_blenlib.h" #include "BLI_float3.hh" #include "BLI_ghash.h" @@ -2269,7 +2270,8 @@ static void gpencil_generate_edgeloops(Object *ob, const int thickness, const float offset, const float matrix[4][4], - const bool use_seams) + const bool use_seams, + const bool use_vgroups) { Mesh *me = (Mesh *)ob->data; if (me->totedge == 0) { @@ -2278,9 +2280,9 @@ static void gpencil_generate_edgeloops(Object *ob, /* Arrays for all edge vertices (forward and backward) that form a edge loop. * This is reused for each edge-loop to create gpencil stroke. */ - uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__); - uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); - uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); + uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * me->totedge * 2, __func__); + uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__); + uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__); /* Create array with all edges. */ GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__); @@ -2311,11 +2313,6 @@ static void gpencil_generate_edgeloops(Object *ob, bool pending = true; int e = 0; while (pending) { - /* Clear arrays of stroke. */ - memset(stroke_fw, 0, sizeof(uint) * me->totedge); - memset(stroke_bw, 0, sizeof(uint) * me->totedge); - memset(stroke, 0, sizeof(uint) * me->totedge * 2); - gped = &gp_edges[e]; /* Look first unused edge. */ if (gped->flag != 0) { @@ -2330,7 +2327,7 @@ static void gpencil_generate_edgeloops(Object *ob, stroke_bw[0] = e; gped->flag = 1; - /* Hash used to avoid loop over same vertice. */ + /* Hash used to avoid loop over same vertices. */ GHash *v_table = BLI_ghash_int_new(__func__); /* Look forward edges. */ int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false); @@ -2354,38 +2351,41 @@ static void gpencil_generate_edgeloops(Object *ob, bGPDstroke *gps_stroke = BKE_gpencil_stroke_add( gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false); + /* Create dvert data. */ + MDeformVert *me_dvert = me->dvert; + if (use_vgroups && me_dvert) { + gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1), + "gp_stroke_dverts"); + } + /* Create first segment. */ float fpt[3]; - uint v = stroke[0]; - gped = &gp_edges[v]; - bGPDspoint *pt = &gps_stroke->points[0]; - mul_v3_v3fl(fpt, gped->n1, offset); - add_v3_v3v3(&pt->x, gped->v1_co, fpt); - mul_m4_v3(matrix, &pt->x); - - pt->pressure = 1.0f; - pt->strength = 1.0f; - - pt = &gps_stroke->points[1]; - mul_v3_v3fl(fpt, gped->n2, offset); - add_v3_v3v3(&pt->x, gped->v2_co, fpt); - mul_m4_v3(matrix, &pt->x); - - pt->pressure = 1.0f; - pt->strength = 1.0f; - - /* Add next segments. */ - for (int i = 1; i < array_len; i++) { - v = stroke[i]; - gped = &gp_edges[v]; - - pt = &gps_stroke->points[i + 1]; - mul_v3_v3fl(fpt, gped->n2, offset); - add_v3_v3v3(&pt->x, gped->v2_co, fpt); + for (int i = 0; i < array_len + 1; i++) { + int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2; + MVert *mv = &me->mvert[vertex_index]; + + /* Add segment. */ + bGPDspoint *pt = &gps_stroke->points[i]; + normal_short_to_float_v3(fpt, mv->no); + mul_v3_v3fl(fpt, fpt, offset); + add_v3_v3v3(&pt->x, mv->co, fpt); mul_m4_v3(matrix, &pt->x); pt->pressure = 1.0f; pt->strength = 1.0f; + + /* Copy vertex groups from mesh. Assuming they already exist in the same order. */ + if (use_vgroups && me_dvert) { + MDeformVert *dv = &gps_stroke->dvert[i]; + MDeformVert *src_dv = &me_dvert[vertex_index]; + dv->totweight = src_dv->totweight; + dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_stroke_dverts_dw"); + for (int j = 0; j < dv->totweight; j++) { + dv->dw[j].weight = src_dv->dw[j].weight; + dv->dw[j].def_nr = src_dv->dw[j].def_nr; + } + } } BKE_gpencil_stroke_geometry_update(gpd, gps_stroke); @@ -2488,7 +2488,8 @@ bool BKE_gpencil_convert_mesh(Main *bmain, const float matrix[4][4], const int frame_offset, const bool use_seams, - const bool use_faces) + const bool use_faces, + const bool use_vgroups) { if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) { return false; @@ -2505,83 +2506,105 @@ bool BKE_gpencil_convert_mesh(Main *bmain, char element_name[200]; /* Need at least an edge. */ - if (me_eval->totvert < 2) { + if (me_eval->totedge < 1) { return false; } + /* Create matching vertex groups. */ + BKE_defgroup_copy_list(&gpd->vertex_group_names, &me_eval->vertex_group_names); + gpd->vertex_group_active_index = me_eval->vertex_group_active_index; + const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}}; - /* Create stroke material. */ + /* Lookup existing stroke material on gp object. */ make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name); int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name); if (stroke_mat_index == -1) { + /* Create new default stroke material as there is no existing material. */ gpencil_add_material( bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index); } /* Export faces as filled strokes. */ - if (use_faces) { - + if (use_faces && mpoly_len > 0) { /* Read all polygons and create fill for each. */ - if (mpoly_len > 0) { - make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name); - /* Create Layer and Frame. */ - bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name); - if (gpl_fill == nullptr) { - gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); - } - bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( - gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); - int i; - for (i = 0; i < mpoly_len; i++) { - const MPoly *mp = &mpoly[i]; - - /* Find material. */ - int mat_idx = 0; - Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1); - make_element_name( - ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name); - mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name); - if (mat_idx == -1) { - float color[4]; - if (ma != nullptr) { - copy_v3_v3(color, &ma->r); - color[3] = 1.0f; - } - else { - copy_v4_v4(color, default_colors[1]); - } - gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx); + make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name); + /* Create Layer and Frame. */ + bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name); + if (gpl_fill == nullptr) { + gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); + } + bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( + gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); + int i; + for (i = 0; i < mpoly_len; i++) { + const MPoly *mp = &mpoly[i]; + + /* Find material. */ + int mat_idx = 0; + Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1); + make_element_name( + ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name); + mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name); + if (mat_idx == -1) { + float color[4]; + if (ma != nullptr) { + copy_v3_v3(color, &ma->r); + color[3] = 1.0f; + } + else { + copy_v4_v4(color, default_colors[1]); } + gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx); + } - bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false); - gps_fill->flag |= GP_STROKE_CYCLIC; + bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false); + gps_fill->flag |= GP_STROKE_CYCLIC; - /* Add points to strokes. */ - for (int j = 0; j < mp->totloop; j++) { - const MLoop *ml = &mloop[mp->loopstart + j]; - const MVert *mv = &me_eval->mvert[ml->v]; + /* Create dvert data. */ + MDeformVert *me_dvert = me_eval->dvert; + if (use_vgroups && me_dvert) { + gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop, + "gp_fill_dverts"); + } - bGPDspoint *pt = &gps_fill->points[j]; - copy_v3_v3(&pt->x, mv->co); - mul_m4_v3(matrix, &pt->x); - pt->pressure = 1.0f; - pt->strength = 1.0f; - } - /* If has only 3 points subdivide. */ - if (mp->totloop == 3) { - BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE); + /* Add points to strokes. */ + for (int j = 0; j < mp->totloop; j++) { + const MLoop *ml = &mloop[mp->loopstart + j]; + const MVert *mv = &me_eval->mvert[ml->v]; + + bGPDspoint *pt = &gps_fill->points[j]; + copy_v3_v3(&pt->x, mv->co); + mul_m4_v3(matrix, &pt->x); + pt->pressure = 1.0f; + pt->strength = 1.0f; + + /* Copy vertex groups from mesh. Assuming they already exist in the same order. */ + if (use_vgroups && me_dvert) { + MDeformVert *dv = &gps_fill->dvert[j]; + MDeformVert *src_dv = &me_dvert[ml->v]; + dv->totweight = src_dv->totweight; + dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_fill_dverts_dw"); + for (int k = 0; k < dv->totweight; k++) { + dv->dw[k].weight = src_dv->dw[k].weight; + dv->dw[k].def_nr = src_dv->dw[k].def_nr; + } } - - BKE_gpencil_stroke_geometry_update(gpd, gps_fill); } + /* If has only 3 points subdivide. */ + if (mp->totloop == 3) { + BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE); + } + + BKE_gpencil_stroke_geometry_update(gpd, gps_fill); } } /* Create stroke from edges. */ - make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name); /* Create Layer and Frame. */ + make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name); bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name); if (gpl_stroke == nullptr) { gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false); @@ -2589,8 +2612,16 @@ bool BKE_gpencil_convert_mesh(Main *bmain, bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get( gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW); - gpencil_generate_edgeloops( - ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams); + gpencil_generate_edgeloops(ob_eval, + gpd, + gpf_stroke, + stroke_mat_index, + angle, + thickness, + offset, + matrix, + use_seams, + use_vgroups); /* Tag for recalculation */ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -2787,46 +2818,12 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps) /* Flip stroke. */ void BKE_gpencil_stroke_flip(bGPDstroke *gps) { - int end = gps->totpoints - 1; + /* Reverse points. */ + BLI_array_reverse(gps->points, gps->totpoints); - for (int i = 0; i < gps->totpoints / 2; i++) { - bGPDspoint *point, *point2; - bGPDspoint pt; - - /* save first point */ - point = &gps->points[i]; - pt.x = point->x; - pt.y = point->y; - pt.z = point->z; - pt.flag = point->flag; - pt.pressure = point->pressure; - pt.strength = point->strength; - pt.time = point->time; - copy_v4_v4(pt.vert_color, point->vert_color); - - /* replace first point with last point */ - point2 = &gps->points[end]; - point->x = point2->x; - point->y = point2->y; - point->z = point2->z; - point->flag = point2->flag; - point->pressure = point2->pressure; - point->strength = point2->strength; - point->time = point2->time; - copy_v4_v4(point->vert_color, point2->vert_color); - - /* replace last point with first saved before */ - point = &gps->points[end]; - point->x = pt.x; - point->y = pt.y; - point->z = pt.z; - point->flag = pt.flag; - point->pressure = pt.pressure; - point->strength = pt.strength; - point->time = pt.time; - copy_v4_v4(point->vert_color, pt.vert_color); - - end--; + /* Reverse vertex groups if available. */ + if (gps->dvert) { + BLI_array_reverse(gps->dvert, gps->totpoints); } } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 2894d6daf23..af7cc0acb57 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -114,32 +114,31 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data) static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Hair *hair = (Hair *)id; - if (hair->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, Hair, id_address, &hair->id); - BKE_id_blend_write(writer, &hair->id); - - /* Direct data */ - CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); - CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); - - BLO_write_pointer_array(writer, hair->totcol, hair->mat); - if (hair->adt) { - BKE_animdata_blend_write(writer, hair->adt); - } - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); - } + CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, Hair, id_address, &hair->id); + BKE_id_blend_write(writer, &hair->id); + + /* Direct data */ + CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id); + CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id); + + BLO_write_pointer_array(writer, hair->totcol, hair->mat); + if (hair->adt) { + BKE_animdata_blend_write(writer, hair->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); + } + if (clayers && clayers != clayers_buff) { + MEM_freeN(clayers); } } diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 12a0a1e3ae7..5a4b2448a73 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -1,4 +1,4 @@ -/* +/* * 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 diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index f4ba1ff8b92..d87290e1eb4 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -229,12 +229,26 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres { Image *ima = (Image *)id; const bool is_undo = BLO_write_is_undo(writer); - if (ima->id.us > 0 || is_undo) { - ImagePackedFile *imapf; - BLI_assert(ima->packedfile == NULL); + /* Clear all data that isn't read to reduce false detection of changed image during memfile undo. + */ + ima->lastused = 0; + ima->cache = NULL; + ima->gpuflag = 0; + BLI_listbase_clear(&ima->anims); + BLI_listbase_clear(&ima->gpu_refresh_areas); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ima->gputexture[i][j] = NULL; + } + } + + ImagePackedFile *imapf; + + BLI_assert(ima->packedfile == NULL); + if (!is_undo) { /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) { + if (ID_IS_OVERRIDE_LIBRARY(ima)) { BLI_listbase_clear(&ima->packedfiles); } else { @@ -244,29 +258,29 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres ima->packedfile = imapf->packedfile; } } + } - /* write LibData */ - BLO_write_id_struct(writer, Image, id_address, &ima->id); - BKE_id_blend_write(writer, &ima->id); + /* write LibData */ + BLO_write_id_struct(writer, Image, id_address, &ima->id); + BKE_id_blend_write(writer, &ima->id); - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { - BLO_write_struct(writer, ImagePackedFile, imapf); - BKE_packedfile_blend_write(writer, imapf->packedfile); - } + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + BLO_write_struct(writer, ImagePackedFile, imapf); + BKE_packedfile_blend_write(writer, imapf->packedfile); + } - BKE_previewimg_blend_write(writer, ima->preview); + BKE_previewimg_blend_write(writer, ima->preview); - LISTBASE_FOREACH (ImageView *, iv, &ima->views) { - BLO_write_struct(writer, ImageView, iv); - } - BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); + LISTBASE_FOREACH (ImageView *, iv, &ima->views) { + BLO_write_struct(writer, ImageView, iv); + } + BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); - BLO_write_struct_list(writer, ImageTile, &ima->tiles); + BLO_write_struct_list(writer, ImageTile, &ima->tiles); - ima->packedfile = NULL; + ima->packedfile = NULL; - BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); - } + BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); } static void image_blend_read_data(BlendDataReader *reader, ID *id) @@ -300,6 +314,7 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { tile->ok = IMA_OK; } + ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); } @@ -519,7 +534,7 @@ void BKE_image_free_buffers(Image *ima) } /** Free (or release) any data used by this image (does not free the image itself). */ -void BKE_image_free(Image *ima) +void BKE_image_free_data(Image *ima) { image_free_data(&ima->id); } @@ -670,24 +685,27 @@ bool BKE_image_has_opengl_texture(Image *ima) return false; } +static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) +{ + BLI_assert(ima != NULL && ima->tiles.first); + ImageTile *tile = ima->tiles.first; + return (iuser && iuser->tile) ? iuser->tile : tile->tile_number; +} + ImageTile *BKE_image_get_tile(Image *ima, int tile_number) { if (ima == NULL) { return NULL; } - /* Verify valid tile range. */ - if ((tile_number != 0) && (tile_number < 1001 || tile_number > IMA_UDIM_MAX)) { - return NULL; - } - - /* Tile number 0 is a special case and refers to the first tile, typically + /* Tiles 0 and 1001 are a special case and refer to the first tile, typically * coming from non-UDIM-aware code. */ if (ELEM(tile_number, 0, 1001)) { return ima->tiles.first; } - if (ima->source != IMA_SRC_TILED) { + /* Must have a tiled image and a valid tile number at this point. */ + if (ima->source != IMA_SRC_TILED || tile_number < 1001 || tile_number > IMA_UDIM_MAX) { return NULL; } @@ -702,7 +720,7 @@ ImageTile *BKE_image_get_tile(Image *ima, int tile_number) ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) { - return BKE_image_get_tile(ima, (iuser && iuser->tile) ? iuser->tile : 1001); + return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser)); } int BKE_image_get_tile_from_pos(struct Image *ima, @@ -1020,7 +1038,7 @@ Image *BKE_image_add_generated(Main *bmain, int view_id; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - /* STRNCPY(ima->filepath, name); */ /* don't do this, this writes in ain invalid filepath! */ + // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */ ima->gen_x = width; ima->gen_y = height; ima->gen_type = gen_type; @@ -3803,8 +3821,8 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return false; } - if (tile == ima->tiles.first) { - /* Can't remove first tile. */ + if (BLI_listbase_is_single(&ima->tiles)) { + /* Can't remove the last remaining tile. */ return false; } @@ -3815,6 +3833,64 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return true; } +void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) +{ + if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) { + return; + } + + if (new_tile_number < 1001 || new_tile_number > IMA_UDIM_MAX) { + return; + } + + const int old_tile_number = tile->tile_number; + tile->tile_number = new_tile_number; + + if (BKE_image_is_multiview(ima)) { + const int totviews = BLI_listbase_count(&ima->views); + for (int i = 0; i < totviews; i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number); + image_remove_ibuf(ima, i, old_tile_number); + image_assign_ibuf(ima, ibuf, i, new_tile_number); + IMB_freeImBuf(ibuf); + } + } + else { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number); + image_remove_ibuf(ima, 0, old_tile_number); + image_assign_ibuf(ima, ibuf, 0, new_tile_number); + IMB_freeImBuf(ibuf); + } + + for (int eye = 0; eye < 2; eye++) { + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + } + } +} + +static int tile_sort_cb(const void *a, const void *b) +{ + const ImageTile *tile_a = a; + const ImageTile *tile_b = b; + return (tile_a->tile_number > tile_b->tile_number) ? 1 : 0; +} + +void BKE_image_sort_tiles(struct Image *ima) +{ + if (ima == NULL || ima->source != IMA_SRC_TILED) { + return; + } + + BLI_listbase_sort(&ima->tiles, tile_sort_cb); +} + bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile, int width, @@ -4890,7 +4966,7 @@ static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry } } else if (ima->source == IMA_SRC_TILED) { - frame = (iuser && iuser->tile) ? iuser->tile : 1001; + frame = image_get_tile_number_from_iuser(ima, iuser); } *r_entry = frame; @@ -4955,7 +5031,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, } else if (ima->source == IMA_SRC_TILED) { if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { - entry = (iuser && iuser->tile) ? iuser->tile : 1001; + entry = image_get_tile_number_from_iuser(ima, iuser); ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); if ((ima->type == IMA_TYPE_IMAGE) && ibuf != NULL) { @@ -5507,7 +5583,7 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) index = iuser ? iuser->framenr : ima->lastframe; } else { - index = (iuser && iuser->tile) ? iuser->tile : 1001; + index = image_get_tile_number_from_iuser(ima, iuser); } BLI_path_sequence_decode(filepath, head, tail, &numlen); diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index bb7495437bb..d179dd40c33 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -108,8 +108,9 @@ static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multivi float array_w = GPU_texture_width(tilearray); float array_h = GPU_texture_height(tilearray); + /* Determine maximum tile number. */ + BKE_image_sort_tiles(ima); ImageTile *last_tile = (ImageTile *)ima->tiles.last; - /* Tiles are sorted by number. */ int max_tile = last_tile->tile_number - 1001; /* create image */ diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c index 360bad3e786..f93ede517a9 100644 --- a/source/blender/blenkernel/intern/image_save.c +++ b/source/blender/blenkernel/intern/image_save.c @@ -404,11 +404,13 @@ bool BKE_image_save( if (ima->source == IMA_SRC_TILED) { /* Verify filepath for tiles images. */ - if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != 1001) { + ImageTile *first_tile = ima->tiles.first; + if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != first_tile->tile_number) { BKE_reportf(reports, RPT_ERROR, - "When saving a tiled image, the path '%s' must contain the UDIM tag 1001", - opts->filepath); + "When saving a tiled image, the path '%s' must contain the UDIM tile number %d", + opts->filepath, + first_tile->tile_number); return false; } @@ -430,9 +432,14 @@ bool BKE_image_save( BLI_path_sequence_decode(filepath, head, tail, &numlen); /* Save all other tiles. */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - /* Tile 1001 was already saved before the loop. */ - if (tile->tile_number == 1001 || !ok) { + int index; + LISTBASE_FOREACH_INDEX (ImageTile *, tile, &ima->tiles, index) { + /* First tile was already saved before the loop. */ + if (index == 0) { + continue; + } + + if (!ok) { continue; } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 0f8c9bad798..f79058dcf21 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -114,27 +114,26 @@ static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_add { Key *key = (Key *)id; const bool is_undo = BLO_write_is_undo(writer); - if (key->id.us > 0 || is_undo) { - /* write LibData */ - BLO_write_id_struct(writer, Key, id_address, &key->id); - BKE_id_blend_write(writer, &key->id); - - if (key->adt) { - BKE_animdata_blend_write(writer, key->adt); - } - - /* direct data */ - LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { - KeyBlock tmp_kb = *kb; - /* Do not store actual geometry data in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) { - tmp_kb.totelem = 0; - tmp_kb.data = NULL; - } - BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb); - if (tmp_kb.data != NULL) { - BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data); - } + + /* write LibData */ + BLO_write_id_struct(writer, Key, id_address, &key->id); + BKE_id_blend_write(writer, &key->id); + + if (key->adt) { + BKE_animdata_blend_write(writer, key->adt); + } + + /* direct data */ + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { + KeyBlock tmp_kb = *kb; + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) { + tmp_kb.totelem = 0; + tmp_kb.data = NULL; + } + BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb); + if (tmp_kb.data != NULL) { + BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data); } } } @@ -246,7 +245,7 @@ typedef struct WeightsArrayCache { } WeightsArrayCache; /** Free (or release) any data used by this shapekey (does not free the key itself). */ -void BKE_key_free(Key *key) +void BKE_key_free_data(Key *key) { shapekey_free_data(&key->id); } @@ -2281,15 +2280,8 @@ void BKE_keyblock_mesh_calc_normals(struct KeyBlock *kb, r_polynors = MEM_mallocN(sizeof(float[3]) * me.totpoly, __func__); free_polynors = true; } - BKE_mesh_calc_normals_poly(me.mvert, - r_vertnors, - me.totvert, - me.mloop, - me.mpoly, - me.totloop, - me.totpoly, - r_polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex( + me.mvert, me.totvert, me.mloop, me.totloop, me.mpoly, me.totpoly, r_polynors, r_vertnors); if (r_loopnors) { short(*clnors)[2] = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); /* May be NULL. */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 9875d776d33..e804f32e5a6 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -137,26 +137,25 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Lattice *lt = (Lattice *)id; - if (lt->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - lt->editlatt = NULL; - lt->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, Lattice, id_address, <->id); - BKE_id_blend_write(writer, <->id); - - /* write animdata */ - if (lt->adt) { - BKE_animdata_blend_write(writer, lt->adt); - } - /* direct data */ - BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + lt->editlatt = NULL; + lt->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, Lattice, id_address, <->id); + BKE_id_blend_write(writer, <->id); - BKE_defbase_blend_write(writer, <->vertex_group_names); - BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + /* write animdata */ + if (lt->adt) { + BKE_animdata_blend_write(writer, lt->adt); } + + /* direct data */ + BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + + BKE_defbase_blend_write(writer, <->vertex_group_names); + BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); } static void lattice_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index ae1863f0a47..b489675cd74 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -602,7 +602,7 @@ static bool layer_collection_hidden(ViewLayer *view_layer, LayerCollection *lc) } /* Check visiblilty restriction flags */ - if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) { + if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_HIDE_VIEWPORT) { return true; } @@ -1005,22 +1005,22 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, BLI_addtail(r_lb_new_object_bases, base); } - if ((collection_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) { + if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) { base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH); if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) { base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER; } - if (((collection_restrict & COLLECTION_RESTRICT_SELECT) == 0)) { + if (((collection_restrict & COLLECTION_HIDE_SELECT) == 0)) { base->flag_from_collection |= BASE_SELECTABLE; } } - if ((collection_restrict & COLLECTION_RESTRICT_RENDER) == 0) { + if ((collection_restrict & COLLECTION_HIDE_RENDER) == 0) { base->flag_from_collection |= BASE_ENABLED_RENDER; } /* Holdout and indirect only */ - if (layer->flag & LAYER_COLLECTION_HOLDOUT) { + if ((layer->flag & LAYER_COLLECTION_HOLDOUT) || (base->object->visibility_flag & OB_HOLDOUT)) { base->flag_from_collection |= BASE_HOLDOUT; } if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { @@ -1150,11 +1150,11 @@ static void layer_collection_sync(ViewLayer *view_layer, /* We separate restrict viewport and visible view layer because a layer collection can be * hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */ - if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) { - child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; + if (child_collection_restrict & COLLECTION_HIDE_VIEWPORT) { + child_layer->runtime_flag |= LAYER_COLLECTION_HIDE_VIEWPORT; } - if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && + if (((child_layer->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0) && ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; } @@ -1174,6 +1174,52 @@ static void layer_collection_sync(ViewLayer *view_layer, parent_local_collections_bits); } +#ifndef NDEBUG +static bool view_layer_objects_base_cache_validate(ViewLayer *view_layer, LayerCollection *layer) +{ + bool is_valid = true; + + if (layer == NULL) { + layer = view_layer->layer_collections.first; + } + + /* Only check for a collection's objects if its layer is not excluded. */ + if ((layer->flag & LAYER_COLLECTION_EXCLUDE) == 0) { + LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) { + if (cob->ob == NULL) { + continue; + } + if (BLI_ghash_lookup(view_layer->object_bases_hash, cob->ob) == NULL) { + CLOG_FATAL( + &LOG, + "Object '%s' from collection '%s' has no entry in view layer's object bases cache", + cob->ob->id.name + 2, + layer->collection->id.name + 2); + is_valid = false; + break; + } + } + } + + if (is_valid) { + LISTBASE_FOREACH (LayerCollection *, layer_child, &layer->layer_collections) { + if (!view_layer_objects_base_cache_validate(view_layer, layer_child)) { + is_valid = false; + break; + } + } + } + + return is_valid; +} +#else +static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer), + LayerCollection *UNUSED(layer)) +{ + return true; +} +#endif + /** * Update view layer collection tree from collections used in the scene. * This is used when collections are removed or added, both while editing @@ -1240,6 +1286,12 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) } if (base->object) { + /* Those asserts are commented, since they are too expensive to perform even in debug, as + * this layer resync function currently gets called way too often. */ +#if 0 + BLI_assert(BLI_findindex(&new_object_bases, base) == -1); + BLI_assert(BLI_findptr(&new_object_bases, base->object, offsetof(Base, object)) == NULL); +#endif BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL); } } @@ -1247,6 +1299,8 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) BLI_freelistN(&view_layer->object_bases); view_layer->object_bases = new_object_bases; + view_layer_objects_base_cache_validate(view_layer, NULL); + LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { BKE_base_eval_flags(base); } @@ -1333,7 +1387,7 @@ void BKE_main_collection_sync_remap(const Main *bmain) */ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection *lc, bool deselect) { - if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) { + if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } @@ -1369,7 +1423,7 @@ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection bool BKE_layer_collection_has_selected_objects(ViewLayer *view_layer, LayerCollection *lc) { - if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) { + if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } @@ -1457,7 +1511,7 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o { BLI_assert(v3d != NULL); - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { return false; } @@ -2146,14 +2200,14 @@ void BKE_base_eval_flags(Base *base) base->flag |= (base->flag_from_collection & g_base_collection_flags); /* Apply object restrictions. */ - const int object_restrict = base->object->restrictflag; - if (object_restrict & OB_RESTRICT_VIEWPORT) { + const int object_restrict = base->object->visibility_flag; + if (object_restrict & OB_HIDE_VIEWPORT) { base->flag &= ~BASE_ENABLED_VIEWPORT; } - if (object_restrict & OB_RESTRICT_RENDER) { + if (object_restrict & OB_HIDE_RENDER) { base->flag &= ~BASE_ENABLED_RENDER; } - if (object_restrict & OB_RESTRICT_SELECT) { + if (object_restrict & OB_HIDE_SELECT) { base->flag &= ~BASE_SELECTABLE; } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 62d29188c5a..0f880d16358 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -73,6 +73,7 @@ #include "BKE_rigidbody.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "RNA_access.h" @@ -141,7 +142,8 @@ static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData * { ID *id = cb_data->user_data; if (*cb_data->id_pointer == id) { - DEG_id_tag_update_ex(cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO); + DEG_id_tag_update_ex( + cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO | ID_RECALC_COPY_ON_WRITE); return IDWALK_RET_STOP_ITER; } return IDWALK_RET_NOP; @@ -193,6 +195,8 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) if (key != NULL) { lib_id_clear_library_data_ex(bmain, &key->id); } + + DEG_relations_tag_update(bmain); } void BKE_lib_id_clear_library_data(Main *bmain, ID *id) @@ -1317,14 +1321,6 @@ void *BKE_libblock_copy(Main *bmain, const ID *id) return idn; } -/* XXX TODO: get rid of this useless wrapper at some point... */ -void *BKE_libblock_copy_for_localize(const ID *id) -{ - ID *idn; - BKE_libblock_copy_ex(NULL, id, &idn, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); - return idn; -} - /* ***************** ID ************************ */ ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name) { diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index a9407860c06..43afac5a376 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -142,14 +142,7 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i DEG_id_type_tag(bmain, type); } -#ifdef WITH_PYTHON -# ifdef WITH_PYTHON_SAFETY - BPY_id_release(id); -# endif - if (id->py_instance) { - BPY_DECREF_RNA_INVALIDATE(id->py_instance); - } -#endif + BKE_libblock_free_data_py(id); Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : NULL; @@ -406,3 +399,29 @@ size_t BKE_id_multi_tagged_delete(Main *bmain) { return id_delete(bmain, true); } + +/* -------------------------------------------------------------------- */ +/** \name Python Data Handling + * \{ */ + +/** + * In most cases #BKE_id_free_ex handles this, when lower level functions are called directly + * this function will need to be called too, if Python has access to the data. + * + * ID data-blocks such as #Material.nodetree are not stored in #Main. + */ +void BKE_libblock_free_data_py(ID *id) +{ +#ifdef WITH_PYTHON +# ifdef WITH_PYTHON_SAFETY + BPY_id_release(id); +# endif + if (id->py_instance) { + BPY_DECREF_RNA_INVALIDATE(id->py_instance); + } +#else + UNUSED_VARS(id); +#endif +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8e67547b719..8083585b594 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -571,7 +571,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat * would use one of those. * NOTE: missing IDs (aka placeholders) are never overridden. */ if (ELEM(GS(to_id->name), ID_OB, ID_GR)) { - if ((to_id->tag & LIB_TAG_MISSING)) { + if (to_id->tag & LIB_TAG_MISSING) { to_id->tag |= missing_tag; } else { @@ -604,7 +604,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) const bool is_resync = data->is_resync; BLI_assert(!data->is_override); - if ((id_root->tag & LIB_TAG_MISSING)) { + if (id_root->tag & LIB_TAG_MISSING) { id_root->tag |= data->missing_tag; } else { @@ -654,7 +654,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) if (instantiating_collection == NULL && instantiating_collection_override_candidate != NULL) { - if ((instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING)) { + if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) { instantiating_collection_override_candidate->id.tag |= data->missing_tag; } else { @@ -730,7 +730,7 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); BLI_assert(data->is_override); - if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { + if (id_root->override_library->reference->tag & LIB_TAG_MISSING) { id_root->tag |= data->missing_tag; } else { @@ -855,8 +855,8 @@ static void lib_override_library_create_post_process(Main *bmain, default_instantiating_collection = BKE_collection_add( bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT | + COLLECTION_HIDE_RENDER; break; } case ID_OB: { @@ -1020,7 +1020,7 @@ bool BKE_lib_override_library_resync(Main *bmain, if (id_root_reference->tag & LIB_TAG_MISSING) { BKE_reportf(reports != NULL ? reports->reports : NULL, RPT_ERROR, - "impossible to resync data-block %s and its dependencies, as its linked reference " + "Impossible to resync data-block %s and its dependencies, as its linked reference " "is missing", id_root->name + 2); return false; @@ -1599,6 +1599,17 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + /* We cannot resync a scene that is currently active. */ + if (id == &scene->id) { + id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; + BKE_reportf(reports->reports, + RPT_WARNING, + "Scene '%s' was not resynced as it is the currently active one", + scene->id.name + 2); + continue; + } + Library *library = id->lib; int level = 0; @@ -1620,7 +1631,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( CLOG_INFO(&LOG, 2, "\tSuccess: %d", success); if (success) { reports->count.resynced_lib_overrides++; - if (library_indirect_level > 0 && + if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list && BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) { BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library); reports->resynced_lib_overrides_libraries_count++; @@ -1731,8 +1742,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, override_resync_residual_storage = BKE_collection_add( bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME); /* Hide the collection from viewport and render. */ - override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + override_resync_residual_storage->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } /* Necessary to improve performances, and prevent layers matching override sub-collections to be diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 977e53c8474..9400458376d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -88,8 +88,8 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int /* 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_EMBEDDED | IDWALK_CB_LOOPBACK | - IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { + if (cb_flag & + (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; } diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index d91d80ac683..c2b71b85973 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -136,27 +136,26 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data) static void light_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Light *la = (Light *)id; - if (la->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Light, id_address, &la->id); - BKE_id_blend_write(writer, &la->id); - if (la->adt) { - BKE_animdata_blend_write(writer, la->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Light, id_address, &la->id); + BKE_id_blend_write(writer, &la->id); - if (la->curfalloff) { - BKE_curvemapping_blend_write(writer, la->curfalloff); - } + if (la->adt) { + BKE_animdata_blend_write(writer, la->adt); + } - /* Node-tree is integral part of lights, no libdata. */ - if (la->nodetree) { - BLO_write_struct(writer, bNodeTree, la->nodetree); - ntreeBlendWrite(writer, la->nodetree); - } + if (la->curfalloff) { + BKE_curvemapping_blend_write(writer, la->curfalloff); + } - BKE_previewimg_blend_write(writer, la->preview); + /* Node-tree is integral part of lights, no libdata. */ + if (la->nodetree) { + BLO_write_struct(writer, bNodeTree, la->nodetree); + ntreeBlendWrite(writer, la->nodetree); } + + BKE_previewimg_blend_write(writer, la->preview); } static void light_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index b09aed82921..15733af8ef0 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -60,14 +60,13 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data) static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address) { LightProbe *prb = (LightProbe *)id; - if (prb->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); - BKE_id_blend_write(writer, &prb->id); - - if (prb->adt) { - BKE_animdata_blend_write(writer, prb->adt); - } + + /* write LibData */ + BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); + BKE_id_blend_write(writer, &prb->id); + + if (prb->adt) { + BKE_animdata_blend_write(writer, prb->adt); } } diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 26d9ab7a8c7..19030fca38b 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -457,28 +457,27 @@ static void write_linestyle_geometry_modifiers(BlendWriter *writer, ListBase *mo static void linestyle_blend_write(BlendWriter *writer, ID *id, const void *id_address) { FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id; - if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); - BKE_id_blend_write(writer, &linestyle->id); - if (linestyle->adt) { - BKE_animdata_blend_write(writer, linestyle->adt); - } + BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); + BKE_id_blend_write(writer, &linestyle->id); - write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); - write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers); - write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers); - write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers); - for (int a = 0; a < MAX_MTEX; a++) { - if (linestyle->mtex[a]) { - BLO_write_struct(writer, MTex, linestyle->mtex[a]); - } - } - if (linestyle->nodetree) { - BLO_write_struct(writer, bNodeTree, linestyle->nodetree); - ntreeBlendWrite(writer, linestyle->nodetree); + if (linestyle->adt) { + BKE_animdata_blend_write(writer, linestyle->adt); + } + + write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); + write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers); + write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers); + write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers); + for (int a = 0; a < MAX_MTEX; a++) { + if (linestyle->mtex[a]) { + BLO_write_struct(writer, MTex, linestyle->mtex[a]); } } + if (linestyle->nodetree) { + BLO_write_struct(writer, bNodeTree, linestyle->nodetree); + ntreeBlendWrite(writer, linestyle->nodetree); + } } static void direct_link_linestyle_color_modifier(BlendDataReader *reader, diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 34dd38164c2..a93fcb6e8e0 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -101,48 +101,47 @@ static void mask_foreach_id(ID *id, LibraryForeachIDData *data) static void mask_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Mask *mask = (Mask *)id; - if (mask->id.us > 0 || BLO_write_is_undo(writer)) { - MaskLayer *masklay; - BLO_write_id_struct(writer, Mask, id_address, &mask->id); - BKE_id_blend_write(writer, &mask->id); + MaskLayer *masklay; - if (mask->adt) { - BKE_animdata_blend_write(writer, mask->adt); - } + BLO_write_id_struct(writer, Mask, id_address, &mask->id); + BKE_id_blend_write(writer, &mask->id); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - MaskLayerShape *masklay_shape; + if (mask->adt) { + BKE_animdata_blend_write(writer, mask->adt); + } - BLO_write_struct(writer, MaskLayer, masklay); + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + MaskLayerShape *masklay_shape; - for (spline = masklay->splines.first; spline; spline = spline->next) { - int i; + BLO_write_struct(writer, MaskLayer, masklay); - void *points_deform = spline->points_deform; - spline->points_deform = NULL; + for (spline = masklay->splines.first; spline; spline = spline->next) { + int i; - BLO_write_struct(writer, MaskSpline, spline); - BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points); + void *points_deform = spline->points_deform; + spline->points_deform = NULL; - spline->points_deform = points_deform; + BLO_write_struct(writer, MaskSpline, spline); + BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points); - for (i = 0; i < spline->tot_point; i++) { - MaskSplinePoint *point = &spline->points[i]; + spline->points_deform = points_deform; - if (point->tot_uw) { - BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw); - } + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (point->tot_uw) { + BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw); } } + } - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - BLO_write_struct(writer, MaskLayerShape, masklay_shape); - BLO_write_float_array( - writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data); - } + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; + masklay_shape = masklay_shape->next) { + BLO_write_struct(writer, MaskLayerShape, masklay_shape); + BLO_write_float_array( + writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data); } } } @@ -429,7 +428,7 @@ MaskLayer *BKE_mask_layer_copy(const MaskLayer *masklay) masklay_new->blend_flag = masklay->blend_flag; masklay_new->flag = masklay->flag; masklay_new->falloff = masklay->falloff; - masklay_new->restrictflag = masklay->restrictflag; + masklay_new->visibility_flag = masklay->visibility_flag; for (spline = masklay->splines.first; spline; spline = spline->next) { MaskSpline *spline_new = BKE_mask_spline_copy(spline); @@ -2092,7 +2091,7 @@ void BKE_mask_clipboard_copy_from_layer(MaskLayer *mask_layer) MaskSpline *spline; /* Nothing to do if selection if disabled for the given layer. */ - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { return; } diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index 81c161a4a7d..e04e5fceec6 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -106,7 +106,7 @@ /* for debugging add... */ #ifndef NDEBUG -/* printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]); \ */ +// printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]); # define FACE_ASSERT(face, vert_max) \ { \ unsigned int *_t = face; \ @@ -292,10 +292,10 @@ static void maskrasterize_spline_differentiate_point_outset(float (*diff_feather co_curr = diff_points[k_curr]; co_next = diff_points[k_next]; - /* sub_v2_v2v2(d_prev, co_prev, co_curr); */ /* precalc */ + // sub_v2_v2v2(d_prev, co_prev, co_curr); /* precalc */ sub_v2_v2v2(d_next, co_curr, co_next); - /* normalize_v2(d_prev); */ /* precalc */ + // normalize_v2(d_prev); /* precalc */ normalize_v2(d_next); if ((do_test == false) || @@ -619,7 +619,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, unsigned int tot_boundary_found = 0; #endif - if (masklay->restrictflag & MASK_RESTRICT_RENDER) { + if (masklay->visibility_flag & MASK_HIDE_RENDER) { /* skip the layer */ mr_handle->layers_tot--; masklay_index--; @@ -1213,7 +1213,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, layer->falloff = masklay->falloff; } - /* printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); */ + // printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); } /* add trianges */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 4f0b2a718ed..13b5bca5638 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -179,31 +179,30 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) static void material_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Material *ma = (Material *)id; - if (ma->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ma->texpaintslot = NULL; - BLI_listbase_clear(&ma->gpumaterial); - /* write LibData */ - BLO_write_id_struct(writer, Material, id_address, &ma->id); - BKE_id_blend_write(writer, &ma->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ma->texpaintslot = NULL; + BLI_listbase_clear(&ma->gpumaterial); - if (ma->adt) { - BKE_animdata_blend_write(writer, ma->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Material, id_address, &ma->id); + BKE_id_blend_write(writer, &ma->id); - /* nodetree is integral part of material, no libdata */ - if (ma->nodetree) { - BLO_write_struct(writer, bNodeTree, ma->nodetree); - ntreeBlendWrite(writer, ma->nodetree); - } + if (ma->adt) { + BKE_animdata_blend_write(writer, ma->adt); + } - BKE_previewimg_blend_write(writer, ma->preview); + /* nodetree is integral part of material, no libdata */ + if (ma->nodetree) { + BLO_write_struct(writer, bNodeTree, ma->nodetree); + ntreeBlendWrite(writer, ma->nodetree); + } - /* grease pencil settings */ - if (ma->gp_style) { - BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style); - } + BKE_previewimg_blend_write(writer, ma->preview); + + /* grease pencil settings */ + if (ma->gp_style) { + BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style); } } @@ -1802,6 +1801,7 @@ void BKE_material_copybuf_free(void) { if (matcopybuf.nodetree) { ntreeFreeLocalTree(matcopybuf.nodetree); + BLI_assert(!matcopybuf.nodetree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(matcopybuf.nodetree); matcopybuf.nodetree = NULL; } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 6a2b56306d6..45cf0f17840 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -119,28 +119,27 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address) { MetaBall *mb = (MetaBall *)id; - if (mb->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; - /* Must always be cleared (meta's don't have their own edit-data). */ - mb->needs_flush_to_id = 0; - mb->lastelem = NULL; - mb->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); - BKE_id_blend_write(writer, &mb->id); - - /* direct data */ - BLO_write_pointer_array(writer, mb->totcol, mb->mat); - if (mb->adt) { - BKE_animdata_blend_write(writer, mb->adt); - } - LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { - BLO_write_struct(writer, MetaElem, ml); - } + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&mb->disp); + mb->editelems = NULL; + /* Must always be cleared (meta's don't have their own edit-data). */ + mb->needs_flush_to_id = 0; + mb->lastelem = NULL; + mb->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); + BKE_id_blend_write(writer, &mb->id); + + /* direct data */ + BLO_write_pointer_array(writer, mb->totcol, mb->mat); + if (mb->adt) { + BKE_animdata_blend_write(writer, mb->adt); + } + + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + BLO_write_struct(writer, MetaElem, ml); } } @@ -289,7 +288,7 @@ void BKE_mball_texspace_calc(Object *ob) bb = ob->runtime.bb; /* Weird one, this. */ - /* INIT_MINMAX(min, max); */ + // INIT_MINMAX(min, max); (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 8d74002ad79..eb8e6aad736 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -179,95 +179,90 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address { Mesh *mesh = (Mesh *)id; const bool is_undo = BLO_write_is_undo(writer); - if (mesh->id.us > 0 || is_undo) { - CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); - flayers = flayers_buff; - - /* Do not store actual geometry data in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { - mesh->mvert = NULL; - mesh->totvert = 0; - memset(&mesh->vdata, 0, sizeof(mesh->vdata)); - vlayers = vlayers_buff; - - mesh->medge = NULL; - mesh->totedge = 0; - memset(&mesh->edata, 0, sizeof(mesh->edata)); - elayers = elayers_buff; - - mesh->mloop = NULL; - mesh->totloop = 0; - memset(&mesh->ldata, 0, sizeof(mesh->ldata)); - llayers = llayers_buff; - - mesh->mpoly = NULL; - mesh->totpoly = 0; - memset(&mesh->pdata, 0, sizeof(mesh->pdata)); - players = players_buff; - } - else { - CustomData_blend_write_prepare( - &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_blend_write_prepare( - &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); - CustomData_blend_write_prepare( - &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_blend_write_prepare( - &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - } - BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); - BKE_id_blend_write(writer, &mesh->id); + CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; + CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - /* direct data */ - if (mesh->adt) { - BKE_animdata_blend_write(writer, mesh->adt); - } + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + flayers = flayers_buff; + + /* Do not store actual geometry data in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) { + mesh->mvert = NULL; + mesh->totvert = 0; + memset(&mesh->vdata, 0, sizeof(mesh->vdata)); + vlayers = vlayers_buff; + + mesh->medge = NULL; + mesh->totedge = 0; + memset(&mesh->edata, 0, sizeof(mesh->edata)); + elayers = elayers_buff; + + mesh->mloop = NULL; + mesh->totloop = 0; + memset(&mesh->ldata, 0, sizeof(mesh->ldata)); + llayers = llayers_buff; + + mesh->mpoly = NULL; + mesh->totpoly = 0; + memset(&mesh->pdata, 0, sizeof(mesh->pdata)); + players = players_buff; + } + else { + CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); + CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); + CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); + CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + } - BKE_defbase_blend_write(writer, &mesh->vertex_group_names); + BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); + BKE_id_blend_write(writer, &mesh->id); - BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); - BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); + /* direct data */ + if (mesh->adt) { + BKE_animdata_blend_write(writer, mesh->adt); + } + + BKE_defbase_blend_write(writer, &mesh->vertex_group_names); - CustomData_blend_write( - writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); - /* fdata is really a dummy - written so slots align */ - CustomData_blend_write( - writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); - CustomData_blend_write( - writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); + BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); + BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); - /* Free temporary data */ + CustomData_blend_write( + writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); + /* fdata is really a dummy - written so slots align */ + CustomData_blend_write( + writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); + CustomData_blend_write( + writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); -/* Free custom-data layers, when not assigned a buffer value. */ + /* Free temporary data */ + + /* Free custom-data layers, when not assigned a buffer value. */ #define CD_LAYERS_FREE(id) \ if (id && id != id##_buff) { \ MEM_freeN(id); \ } \ ((void)0) - CD_LAYERS_FREE(vlayers); - CD_LAYERS_FREE(elayers); - /* CD_LAYER_FREE(flayers); */ /* Never allocated. */ - CD_LAYERS_FREE(llayers); - CD_LAYERS_FREE(players); + CD_LAYERS_FREE(vlayers); + CD_LAYERS_FREE(elayers); + // CD_LAYER_FREE(flayers); /* Never allocated. */ + CD_LAYERS_FREE(llayers); + CD_LAYERS_FREE(players); #undef CD_LAYERS_FREE - } } static void mesh_blend_read_data(BlendDataReader *reader, ID *id) @@ -389,6 +384,7 @@ enum { MESHCMP_EDGEUNKNOWN, MESHCMP_VERTCOMISMATCH, MESHCMP_CDLAYERS_MISMATCH, + MESHCMP_ATTRIBUTE_VALUE_MISMATCH, }; static const char *cmpcode_to_str(int code) @@ -416,6 +412,8 @@ static const char *cmpcode_to_str(int code) return "Vertex Coordinate Mismatch"; case MESHCMP_CDLAYERS_MISMATCH: return "CustomData Layer Count Mismatch"; + case MESHCMP_ATTRIBUTE_VALUE_MISMATCH: + return "Attribute Value Mismatch"; default: return "Mesh Comparison Code Unknown"; } @@ -423,171 +421,233 @@ static const char *cmpcode_to_str(int code) /** Thresh is threshold for comparing vertices, UV's, vertex colors, weights, etc. */ static int customdata_compare( - CustomData *c1, CustomData *c2, Mesh *m1, Mesh *m2, const float thresh) + CustomData *c1, CustomData *c2, const int total_length, Mesh *m1, Mesh *m2, const float thresh) { const float thresh_sq = thresh * thresh; CustomDataLayer *l1, *l2; - int i, i1 = 0, i2 = 0, tot, j; - - for (i = 0; i < c1->totlayer; i++) { - if (ELEM(c1->layers[i].type, - CD_MVERT, - CD_MEDGE, - CD_MPOLY, - CD_MLOOPUV, - CD_MLOOPCOL, - CD_MDEFORMVERT)) { - i1++; + int layer_count1 = 0, layer_count2 = 0, j; + const uint64_t cd_mask_non_generic = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | + CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MDEFORMVERT; + const uint64_t cd_mask_all_attr = CD_MASK_PROP_ALL | cd_mask_non_generic; + + for (int i = 0; i < c1->totlayer; i++) { + if (CD_TYPE_AS_MASK(c1->layers[i].type) & cd_mask_all_attr) { + layer_count1++; } } - for (i = 0; i < c2->totlayer; i++) { - if (ELEM(c2->layers[i].type, - CD_MVERT, - CD_MEDGE, - CD_MPOLY, - CD_MLOOPUV, - CD_MLOOPCOL, - CD_MDEFORMVERT)) { - i2++; + for (int i = 0; i < c2->totlayer; i++) { + if (CD_TYPE_AS_MASK(c2->layers[i].type) & cd_mask_all_attr) { + layer_count2++; } } - if (i1 != i2) { + if (layer_count1 != layer_count2) { return MESHCMP_CDLAYERS_MISMATCH; } l1 = c1->layers; l2 = c2->layers; - tot = i1; - i1 = 0; - i2 = 0; - for (i = 0; i < tot; i++) { - while ( - i1 < c1->totlayer && - !ELEM(l1->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) { - i1++; - l1++; - } - - while ( - i2 < c2->totlayer && - !ELEM(l2->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) { - i2++; - l2++; - } - if (l1->type == CD_MVERT) { - MVert *v1 = l1->data; - MVert *v2 = l2->data; - int vtot = m1->totvert; - - for (j = 0; j < vtot; j++, v1++, v2++) { - if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) { - return MESHCMP_VERTCOMISMATCH; - } - /* I don't care about normals, let's just do coordinates. */ + for (int i1 = 0; i1 < c1->totlayer; i1++) { + l1 = c1->layers + i1; + for (int i2 = 0; i2 < c2->totlayer; i2++) { + l2 = c2->layers + i2; + if (l1->type != l2->type || !STREQ(l1->name, l2->name)) { + continue; } - } + /* At this point `l1` and `l2` have the same name and type, so they should be compared. */ - /* We're order-agnostic for edges here. */ - if (l1->type == CD_MEDGE) { - MEdge *e1 = l1->data; - MEdge *e2 = l2->data; - int etot = m1->totedge; - EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot); + switch (l1->type) { - for (j = 0; j < etot; j++, e1++) { - BLI_edgehash_insert(eh, e1->v1, e1->v2, e1); - } + case CD_MVERT: { + MVert *v1 = l1->data; + MVert *v2 = l2->data; + int vtot = m1->totvert; - for (j = 0; j < etot; j++, e2++) { - if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) { - return MESHCMP_EDGEUNKNOWN; + for (j = 0; j < vtot; j++, v1++, v2++) { + if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) { + return MESHCMP_VERTCOMISMATCH; + } + /* I don't care about normals, let's just do coordinates. */ + } + break; } - } - BLI_edgehash_free(eh, NULL); - } - if (l1->type == CD_MPOLY) { - MPoly *p1 = l1->data; - MPoly *p2 = l2->data; - int ptot = m1->totpoly; + /* We're order-agnostic for edges here. */ + case CD_MEDGE: { + MEdge *e1 = l1->data; + MEdge *e2 = l2->data; + int etot = m1->totedge; + EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot); - for (j = 0; j < ptot; j++, p1++, p2++) { - MLoop *lp1, *lp2; - int k; + for (j = 0; j < etot; j++, e1++) { + BLI_edgehash_insert(eh, e1->v1, e1->v2, e1); + } - if (p1->totloop != p2->totloop) { - return MESHCMP_POLYMISMATCH; + for (j = 0; j < etot; j++, e2++) { + if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) { + return MESHCMP_EDGEUNKNOWN; + } + } + BLI_edgehash_free(eh, NULL); + break; } - - lp1 = m1->mloop + p1->loopstart; - lp2 = m2->mloop + p2->loopstart; - - for (k = 0; k < p1->totloop; k++, lp1++, lp2++) { - if (lp1->v != lp2->v) { - return MESHCMP_POLYVERTMISMATCH; + case CD_MPOLY: { + MPoly *p1 = l1->data; + MPoly *p2 = l2->data; + int ptot = m1->totpoly; + + for (j = 0; j < ptot; j++, p1++, p2++) { + MLoop *lp1, *lp2; + int k; + + if (p1->totloop != p2->totloop) { + return MESHCMP_POLYMISMATCH; + } + + lp1 = m1->mloop + p1->loopstart; + lp2 = m2->mloop + p2->loopstart; + + for (k = 0; k < p1->totloop; k++, lp1++, lp2++) { + if (lp1->v != lp2->v) { + return MESHCMP_POLYVERTMISMATCH; + } + } } + break; } - } - } - if (l1->type == CD_MLOOP) { - MLoop *lp1 = l1->data; - MLoop *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (lp1->v != lp2->v) { - return MESHCMP_LOOPMISMATCH; + case CD_MLOOP: { + MLoop *lp1 = l1->data; + MLoop *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (lp1->v != lp2->v) { + return MESHCMP_LOOPMISMATCH; + } + } + break; } - } - } - if (l1->type == CD_MLOOPUV) { - MLoopUV *lp1 = l1->data; - MLoopUV *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) { - return MESHCMP_LOOPUVMISMATCH; + case CD_MLOOPUV: { + MLoopUV *lp1 = l1->data; + MLoopUV *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) { + return MESHCMP_LOOPUVMISMATCH; + } + } + break; } - } - } - - if (l1->type == CD_MLOOPCOL) { - MLoopCol *lp1 = l1->data; - MLoopCol *lp2 = l2->data; - int ltot = m1->totloop; - - for (j = 0; j < ltot; j++, lp1++, lp2++) { - if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh || - abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) { - return MESHCMP_LOOPCOLMISMATCH; + case CD_MLOOPCOL: { + MLoopCol *lp1 = l1->data; + MLoopCol *lp2 = l2->data; + int ltot = m1->totloop; + + for (j = 0; j < ltot; j++, lp1++, lp2++) { + if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh || + abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) { + return MESHCMP_LOOPCOLMISMATCH; + } + } + break; } - } - } - - if (l1->type == CD_MDEFORMVERT) { - MDeformVert *dv1 = l1->data; - MDeformVert *dv2 = l2->data; - int dvtot = m1->totvert; - - for (j = 0; j < dvtot; j++, dv1++, dv2++) { - int k; - MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw; - - if (dv1->totweight != dv2->totweight) { - return MESHCMP_DVERT_TOTGROUPMISMATCH; + case CD_MDEFORMVERT: { + MDeformVert *dv1 = l1->data; + MDeformVert *dv2 = l2->data; + int dvtot = m1->totvert; + + for (j = 0; j < dvtot; j++, dv1++, dv2++) { + int k; + MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw; + + if (dv1->totweight != dv2->totweight) { + return MESHCMP_DVERT_TOTGROUPMISMATCH; + } + + for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) { + if (dw1->def_nr != dw2->def_nr) { + return MESHCMP_DVERT_GROUPMISMATCH; + } + if (fabsf(dw1->weight - dw2->weight) > thresh) { + return MESHCMP_DVERT_WEIGHTMISMATCH; + } + } + } + break; } - - for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) { - if (dw1->def_nr != dw2->def_nr) { - return MESHCMP_DVERT_GROUPMISMATCH; + case CD_PROP_FLOAT: { + const float *l1_data = l1->data; + const float *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + if (fabsf(l1_data[i] - l2_data[i]) > thresh) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_FLOAT2: { + const float(*l1_data)[2] = l1->data; + const float(*l2_data)[2] = l2->data; + + for (int i = 0; i < total_length; i++) { + if (len_squared_v2v2(l1_data[i], l2_data[i]) > thresh_sq) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_FLOAT3: { + const float(*l1_data)[3] = l1->data; + const float(*l2_data)[3] = l2->data; + + for (int i = 0; i < total_length; i++) { + if (len_squared_v3v3(l1_data[i], l2_data[i]) > thresh_sq) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_INT32: { + const int *l1_data = l1->data; + const int *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + if (l1_data[i] != l2_data[i]) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } } - if (fabsf(dw1->weight - dw2->weight) > thresh) { - return MESHCMP_DVERT_WEIGHTMISMATCH; + break; + } + case CD_PROP_BOOL: { + const bool *l1_data = l1->data; + const bool *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + if (l1_data[i] != l2_data[i]) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } + break; + } + case CD_PROP_COLOR: { + const MPropCol *l1_data = l1->data; + const MPropCol *l2_data = l2->data; + + for (int i = 0; i < total_length; i++) { + for (j = 0; j < 4; j++) { + if (fabsf(l1_data[i].color[j] - l2_data[i].color[j]) > thresh) { + return MESHCMP_ATTRIBUTE_VALUE_MISMATCH; + } + } } + break; + } + default: { + break; } } } @@ -626,19 +686,19 @@ const char *BKE_mesh_cmp(Mesh *me1, Mesh *me2, float thresh) return "Number of loops don't match"; } - if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1->totvert, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->edata, &me2->edata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->edata, &me2->edata, me1->totedge, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1->totloop, me1, me2, thresh))) { return cmpcode_to_str(c); } - if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1, me2, thresh))) { + if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1->totpoly, me1, me2, thresh))) { return cmpcode_to_str(c); } @@ -801,8 +861,11 @@ bool BKE_mesh_has_custom_loop_normals(Mesh *me) return CustomData_has_layer(&me->ldata, CD_CUSTOMLOOPNORMAL); } -/** Free (or release) any data used by this mesh (does not free the mesh itself). */ -void BKE_mesh_free(Mesh *me) +/** + * Free (or release) any data used by this mesh (does not free the mesh itself). + * Only use for undo, in most cases `BKE_id_free(NULL, me)` should be used. + */ +void BKE_mesh_free_data_for_undo(Mesh *me) { mesh_free_data(&me->id); } @@ -890,7 +953,7 @@ Mesh *BKE_mesh_new_nomain( NULL, ID_ME, BKE_idtype_idcode_to_name(ID_ME), LIB_ID_CREATE_LOCALIZE); BKE_libblock_init_empty(&mesh->id); - /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */ + /* Don't use #CustomData_reset because we don't want to touch custom-data. */ copy_vn_i(mesh->vdata.typemap, CD_NUMTYPES, -1); copy_vn_i(mesh->edata.typemap, CD_NUMTYPES, -1); copy_vn_i(mesh->fdata.typemap, CD_NUMTYPES, -1); @@ -1018,7 +1081,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval) { /* Evaluated mesh may point to edit mesh, but never owns it. */ mesh_eval->edit_mesh = NULL; - BKE_mesh_free(mesh_eval); + mesh_free_data(&mesh_eval->id); BKE_libblock_free_data(&mesh_eval->id, false); MEM_freeN(mesh_eval); } @@ -1621,10 +1684,7 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh) void BKE_mesh_mselect_clear(Mesh *me) { - if (me->mselect) { - MEM_freeN(me->mselect); - me->mselect = NULL; - } + MEM_SAFE_FREE(me->mselect); me->totselect = 0; } @@ -1841,15 +1901,14 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac } else { polynors = MEM_malloc_arrayN(mesh->totpoly, sizeof(float[3]), __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, - NULL, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + polynors, + NULL); free_polynors = true; } diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 0e4fe91e577..4b1eb5b39ce 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -38,6 +38,7 @@ #include "BLI_utildefines.h" #include "BKE_DerivedMesh.h" +#include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -271,8 +272,8 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totvert == 0) { - /* error("can't convert"); */ - /* Make Sure you check ob->data is a curve */ + /* Make Sure you check ob->data is a curve. */ + // error("can't convert"); return -1; } @@ -1665,6 +1666,10 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* skip the listbase */ MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev); + BLI_freelistN(&mesh_dst->vertex_group_names); + BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); + mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index; + if (take_ownership) { if (alloctype == CD_ASSIGN) { CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask); diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index 9aeaa1ada52..b20d81e7b9c 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -393,15 +393,14 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, /* calculate custom normals into loop_normals, then mirror first half into second half */ - BKE_mesh_calc_normals_poly(result->mvert, - NULL, - result->totvert, - result->mloop, - result->mpoly, - totloop, - totpoly, - poly_normals, - false); + BKE_mesh_calc_normals_poly_and_vertex(result->mvert, + result->totvert, + result->mloop, + totloop, + result->mpoly, + totpoly, + poly_normals, + NULL); BKE_mesh_normals_loop_split(result->mvert, result->totvert, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index f496d6eada1..9a761c6fa11 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -27,8 +27,6 @@ #include <climits> -#include "CLG_log.h" - #include "MEM_guardedalloc.h" #include "DNA_mesh_types.h" @@ -50,6 +48,8 @@ #include "BKE_global.h" #include "BKE_mesh.h" +#include "atomic_ops.h" + // #define DEBUG_TIME #ifdef DEBUG_TIME @@ -57,319 +57,257 @@ # include "PIL_time_utildefines.h" #endif -static CLG_LogRef LOG = {"bke.mesh_normals"}; - /* -------------------------------------------------------------------- */ -/** \name Mesh Normal Calculation +/** \name Private Utility Functions * \{ */ /** - * Call when there are no polygons. + * A thread-safe version of #add_v3_v3 that uses a spin-lock. + * + * \note Avoid using this when the chance of contention is high. */ -static void mesh_calc_normals_vert_fallback(MVert *mverts, int numVerts) +static void add_v3_v3_atomic(float r[3], const float a[3]) { - for (int i = 0; i < numVerts; i++) { - MVert *mv = &mverts[i]; - float no[3]; +#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb)) - normalize_v3_v3(no, mv->co); - normal_float_to_short_v3(mv->no, no); + float virtual_lock = r[0]; + while (true) { + /* This loops until following conditions are met: + * - `r[0]` has same value as virtual_lock (i.e. it did not change since last try). + * - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */ + const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX); + if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) { + break; + } + virtual_lock = test_lock; } -} + virtual_lock += a[0]; + r[1] += a[1]; + r[2] += a[2]; -/* TODO(Sybren): we can probably rename this to BKE_mesh_calc_normals_mapping(), - * and remove the function of the same name below, as that one doesn't seem to be - * called anywhere. */ -void BKE_mesh_calc_normals_mapping_simple(struct Mesh *mesh) -{ - const bool only_face_normals = CustomData_is_referenced_layer(&mesh->vdata, CD_MVERT); - - BKE_mesh_calc_normals_mapping_ex(mesh->mvert, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - mesh->mface, - mesh->totface, - nullptr, - nullptr, - only_face_normals); -} + /* Second atomic operation to 'release' + * our lock on that vector and set its first scalar value. */ + /* Note that we do not need to loop here, since we 'locked' `r[0]`, + * nobody should have changed it in the mean time. */ + virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock); + BLI_assert(virtual_lock == FLT_MAX); -/* Calculate vertex and face normals, face normals are returned in *r_faceNors if non-nullptr - * and vertex normals are stored in actual mverts. - */ -void BKE_mesh_calc_normals_mapping(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3]) -{ - BKE_mesh_calc_normals_mapping_ex(mverts, - numVerts, - mloop, - mpolys, - numLoops, - numPolys, - r_polyNors, - mfaces, - numFaces, - origIndexFace, - r_faceNors, - false); +#undef FLT_EQ_NONAN } -/* extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals */ -void BKE_mesh_calc_normals_mapping_ex(MVert *mverts, - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polyNors)[3], - const MFace *mfaces, - int numFaces, - const int *origIndexFace, - float (*r_faceNors)[3], - const bool only_face_normals) -{ - float(*pnors)[3] = r_polyNors, (*fnors)[3] = r_faceNors; - - if (numPolys == 0) { - if (only_face_normals == false) { - mesh_calc_normals_vert_fallback(mverts, numVerts); - } - return; - } - /* if we are not calculating verts and no verts were passes then we have nothing to do */ - if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) { - CLOG_WARN(&LOG, "called with nothing to do"); - return; - } - - if (!pnors) { - pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__); - } - /* NO NEED TO ALLOC YET */ - /* if (!fnors) fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); */ +/** \} */ - if (only_face_normals == false) { - /* vertex normals are optional, they require some extra calculations, - * so make them optional */ - BKE_mesh_calc_normals_poly( - mverts, nullptr, numVerts, mloop, mpolys, numLoops, numPolys, pnors, false); - } - else { - /* only calc poly normals */ - const MPoly *mp = mpolys; - for (int i = 0; i < numPolys; i++, mp++) { - BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]); - } - } +/* -------------------------------------------------------------------- */ +/** \name Public Utility Functions + * + * Related to managing normals but not directly related to calculating normals. + * \{ */ - if (origIndexFace && - /* fnors == r_faceNors */ /* NO NEED TO ALLOC YET */ - fnors != nullptr && - numFaces) { - const MFace *mf = mfaces; - for (int i = 0; i < numFaces; i++, mf++, origIndexFace++) { - if (*origIndexFace < numPolys) { - copy_v3_v3(fnors[i], pnors[*origIndexFace]); - } - else { - /* eek, we're not corresponding to polys */ - CLOG_ERROR(&LOG, "tessellation face indices are incorrect. normals may look bad."); - } - } - } +void BKE_mesh_normals_tag_dirty(Mesh *mesh) +{ + mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; +} - if (pnors != r_polyNors) { - MEM_freeN(pnors); - } - /* if (fnors != r_faceNors) MEM_freeN(fnors); */ /* NO NEED TO ALLOC YET */ +/** \} */ - fnors = pnors = nullptr; -} +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation (Polygons) + * \{ */ -struct MeshCalcNormalsData { - const MPoly *mpolys; +struct MeshCalcNormalsData_Poly { + const MVert *mvert; const MLoop *mloop; - MVert *mverts; + const MPoly *mpoly; + + /** Polygon normal output. */ float (*pnors)[3]; - float (*lnors_weighted)[3]; - float (*vnors)[3]; }; -static void mesh_calc_normals_poly_cb(void *__restrict userdata, +static void mesh_calc_normals_poly_fn(void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls)) { - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; + const MeshCalcNormalsData_Poly *data = (MeshCalcNormalsData_Poly *)userdata; + const MPoly *mp = &data->mpoly[pidx]; + BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mvert, data->pnors[pidx]); +} + +void BKE_mesh_calc_normals_poly(const MVert *mvert, + int UNUSED(mvert_len), + const MLoop *mloop, + int UNUSED(mloop_len), + const MPoly *mpoly, + int mpoly_len, + float (*r_poly_normals)[3]) +{ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1024; + + BLI_assert((r_poly_normals != nullptr) || (mpoly_len == 0)); + + MeshCalcNormalsData_Poly data = {}; + data.mpoly = mpoly; + data.mloop = mloop; + data.mvert = mvert; + data.pnors = r_poly_normals; - BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]); + BLI_task_parallel_range(0, mpoly_len, &data, mesh_calc_normals_poly_fn, &settings); } -static void mesh_calc_normals_poly_prepare_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict UNUSED(tls)) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation (Polygons & Vertices) + * + * Implement #BKE_mesh_calc_normals_poly_and_vertex, + * + * Take care making optimizations to this function as improvements to low-poly + * meshes can slow down high-poly meshes. For details on performance, see D11993. + * \{ */ + +struct MeshCalcNormalsData_PolyAndVertex { + /** Write into vertex normals #MVert.no. */ + MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly; + + /** Polygon normal output. */ + float (*pnors)[3]; + /** Vertex normal output (may be freed, copied into #MVert.no). */ + float (*vnors)[3]; +}; + +static void mesh_calc_normals_poly_and_vertex_accum_fn( + void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls)) { - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; - const MPoly *mp = &data->mpolys[pidx]; + const MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; + const MPoly *mp = &data->mpoly[pidx]; const MLoop *ml = &data->mloop[mp->loopstart]; - const MVert *mverts = data->mverts; + const MVert *mverts = data->mvert; + float(*vnors)[3] = data->vnors; float pnor_temp[3]; float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp; - float(*lnors_weighted)[3] = data->lnors_weighted; - const int nverts = mp->totloop; - float(*edgevecbuf)[3] = (float(*)[3])BLI_array_alloca(edgevecbuf, (size_t)nverts); + const int i_end = mp->totloop - 1; - /* Polygon Normal and edge-vector */ - /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */ + /* Polygon Normal and edge-vector. */ + /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */ { - int i_prev = nverts - 1; - const float *v_prev = mverts[ml[i_prev].v].co; - const float *v_curr; - zero_v3(pnor); /* Newell's Method */ - for (int i = 0; i < nverts; i++) { - v_curr = mverts[ml[i].v].co; - add_newell_cross_v3_v3v3(pnor, v_prev, v_curr); - - /* Unrelated to normalize, calculate edge-vector */ - sub_v3_v3v3(edgevecbuf[i_prev], v_prev, v_curr); - normalize_v3(edgevecbuf[i_prev]); - i_prev = i; - - v_prev = v_curr; + const float *v_curr = mverts[ml[i_end].v].co; + for (int i_next = 0; i_next <= i_end; i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + add_newell_cross_v3_v3v3(pnor, v_curr, v_next); + v_curr = v_next; } if (UNLIKELY(normalize_v3(pnor) == 0.0f)) { - pnor[2] = 1.0f; /* other axes set to 0.0 */ + pnor[2] = 1.0f; /* Other axes set to zero. */ } } - /* accumulate angle weighted face normal */ - /* inline version of #accumulate_vertex_normals_poly_v3, - * split between this threaded callback and #mesh_calc_normals_poly_accum_cb. */ + /* Accumulate angle weighted face normal into the vertex normal. */ + /* Inline version of #accumulate_vertex_normals_poly_v3. */ { - const float *prev_edge = edgevecbuf[nverts - 1]; - - for (int i = 0; i < nverts; i++) { - const int lidx = mp->loopstart + i; - const float *cur_edge = edgevecbuf[i]; - - /* calculate angle between the two poly edges incident on - * this vertex */ - const float fac = saacos(-dot_v3v3(cur_edge, prev_edge)); + float edvec_prev[3], edvec_next[3], edvec_end[3]; + const float *v_curr = mverts[ml[i_end].v].co; + sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr); + normalize_v3(edvec_prev); + copy_v3_v3(edvec_end, edvec_prev); + + for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) { + const float *v_next = mverts[ml[i_next].v].co; + + /* Skip an extra normalization by reusing the first calculated edge. */ + if (i_next != i_end) { + sub_v3_v3v3(edvec_next, v_curr, v_next); + normalize_v3(edvec_next); + } + else { + copy_v3_v3(edvec_next, edvec_end); + } - /* Store for later accumulation */ - mul_v3_v3fl(lnors_weighted[lidx], pnor, fac); + /* Calculate angle between the two poly edges incident on this vertex. */ + const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next)); + const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac}; - prev_edge = cur_edge; + add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add); + v_curr = v_next; + copy_v3_v3(edvec_prev, edvec_next); } } } -static void mesh_calc_normals_poly_finalize_cb(void *__restrict userdata, - const int vidx, - const TaskParallelTLS *__restrict UNUSED(tls)) +static void mesh_calc_normals_poly_and_vertex_finalize_fn( + void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict UNUSED(tls)) { - MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata; + MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; - MVert *mv = &data->mverts[vidx]; + MVert *mv = &data->mvert[vidx]; float *no = data->vnors[vidx]; if (UNLIKELY(normalize_v3(no) == 0.0f)) { - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ normalize_v3_v3(no, mv->co); } normal_float_to_short_v3(mv->no, no); } -void BKE_mesh_calc_normals_poly(MVert *mverts, - float (*r_vertnors)[3], - int numVerts, - const MLoop *mloop, - const MPoly *mpolys, - int numLoops, - int numPolys, - float (*r_polynors)[3], - const bool only_face_normals) +void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert, + const int mvert_len, + const MLoop *mloop, + const int UNUSED(mloop_len), + const MPoly *mpoly, + const int mpoly_len, + float (*r_poly_normals)[3], + float (*r_vert_normals)[3]) { - float(*pnors)[3] = r_polynors; - TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); settings.min_iter_per_thread = 1024; - if (only_face_normals) { - BLI_assert((pnors != nullptr) || (numPolys == 0)); - BLI_assert(r_vertnors == nullptr); - - MeshCalcNormalsData data; - data.mpolys = mpolys; - data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_cb, &settings); - return; - } - - float(*vnors)[3] = r_vertnors; - float(*lnors_weighted)[3] = (float(*)[3])MEM_malloc_arrayN( - (size_t)numLoops, sizeof(*lnors_weighted), __func__); + float(*vnors)[3] = r_vert_normals; bool free_vnors = false; - /* first go through and calculate normals for all the polys */ + /* First go through and calculate normals for all the polys. */ if (vnors == nullptr) { - vnors = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*vnors), __func__); + vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__); free_vnors = true; } else { - memset(vnors, 0, sizeof(*vnors) * (size_t)numVerts); + memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len); } - MeshCalcNormalsData data; - data.mpolys = mpolys; + MeshCalcNormalsData_PolyAndVertex data = {}; + data.mpoly = mpoly; data.mloop = mloop; - data.mverts = mverts; - data.pnors = pnors; - data.lnors_weighted = lnors_weighted; + data.mvert = mvert; + data.pnors = r_poly_normals; data.vnors = vnors; - /* Compute poly normals, and prepare weighted loop normals. */ - BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_prepare_cb, &settings); - - /* Actually accumulate weighted loop normals into vertex ones. */ - /* Unfortunately, not possible to thread that - * (not in a reasonable, totally lock- and barrier-free fashion), - * since several loops will point to the same vertex... */ - for (int lidx = 0; lidx < numLoops; lidx++) { - add_v3_v3(vnors[mloop[lidx].v], data.lnors_weighted[lidx]); - } + /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + BLI_task_parallel_range( + 0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings); - /* Normalize and validate computed vertex normals. */ - BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings); + /* Normalize and validate computed vertex normals (`vnors`). */ + BLI_task_parallel_range( + 0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings); if (free_vnors) { MEM_freeN(vnors); } - MEM_freeN(lnors_weighted); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Normal Calculation + * \{ */ + void BKE_mesh_ensure_normals(Mesh *mesh) { if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { @@ -410,16 +348,26 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) (size_t)mesh->totpoly, sizeof(*poly_nors), __func__); } - /* calculate poly/vert normals */ - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - poly_nors, - !do_vert_normals); + /* Calculate poly/vert normals. */ + if (do_vert_normals) { + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_nors, + nullptr); + } + else { + BKE_mesh_calc_normals_poly(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + poly_nors); + } if (do_add_poly_nors_cddata) { CustomData_add_layer(&mesh->pdata, CD_NORMAL, CD_ASSIGN, poly_nors, mesh->totpoly); @@ -430,22 +378,23 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh) } } -/* Note that this does not update the CD_NORMAL layer, - * but does update the normals in the CD_MVERT layer. */ +/** + * NOTE: this does not update the #CD_NORMAL layer, + * but does update the normals in the #CD_MVERT layer. + */ void BKE_mesh_calc_normals(Mesh *mesh) { #ifdef DEBUG_TIME TIMEIT_START_AVERAGED(BKE_mesh_calc_normals); #endif - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - nullptr, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + nullptr, + nullptr); #ifdef DEBUG_TIME TIMEIT_END_AVERAGED(BKE_mesh_calc_normals); #endif @@ -488,7 +437,7 @@ void BKE_mesh_calc_normals_looptri(MVert *mverts, mverts[vtri[2]].co); } - /* following Mesh convention; we use vertex coordinate itself for normal in this case */ + /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */ for (int i = 0; i < numVerts; i++) { MVert *mv = &mverts[i]; float *no = tnorms[i]; @@ -628,11 +577,11 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space, BLI_stack_discard(edge_vectors); nbr++; } - /* NOTE: In theory, this could be 'nbr > 2', - * but there is one case where we only have two edges for two loops: - * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). + /* NOTE: In theory, this could be `nbr > 2`, + * but there is one case where we only have two edges for two loops: + * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.). */ - BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop... */ + BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop. */ lnor_space->ref_alpha = alpha / (float)nbr; } else { @@ -704,7 +653,7 @@ MINLINE float unit_short_to_float(const short val) MINLINE short unit_float_to_short(const float val) { - /* Rounding... */ + /* Rounding. */ return (short)floorf(val * (float)SHRT_MAX + 0.5f); } @@ -915,7 +864,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, e2l[1] = INDEX_INVALID; /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ + * other causes than angle threshold. */ if (do_sharp_edges_tag && is_angle_sharp) { BLI_BITMAP_SET(sharp_edges, ml_curr->e, true); } @@ -929,7 +878,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data, e2l[1] = INDEX_INVALID; /* We want to avoid tagging edges as sharp when it is already defined as such by - * other causes than angle threshold... */ + * other causes than angle threshold. */ if (do_sharp_edges_tag) { BLI_BITMAP_SET(sharp_edges, ml_curr->e, false); } @@ -981,7 +930,7 @@ void BKE_edges_sharp_from_angle_set(const struct MVert *mverts, /* Simple mapping from a loop to its polygon index. */ int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__); - LoopSplitTaskDataCommon common_data; + LoopSplitTaskDataCommon common_data = {}; common_data.mverts = mverts; common_data.medges = medges; common_data.mloops = mloops; @@ -1011,14 +960,13 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, const MLoop *mlfan_next; const MPoly *mpfan_next; - /* Warning! This is rather complex! + /* WARNING: This is rather complex! * We have to find our next edge around the vertex (fan mode). * First we find the next loop, which is either previous or next to mlfan_curr_index, depending * whether both loops using current edge are in the same direction or not, and whether * mlfan_curr_index actually uses the vertex we are fanning around! * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one - * (i.e. not the future mlfan_curr)... - */ + * (i.e. not the future `mlfan_curr`). */ *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; @@ -1043,7 +991,7 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops, *r_mlfan_vert_index = *r_mlfan_curr_index; } *r_mlfan_curr = &mloops[*r_mlfan_curr_index]; - /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ + /* And now we are back in sync, mlfan_curr_index is the index of `mlfan_curr`! Pff! */ } static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) @@ -1098,8 +1046,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS normalize_v3(vec_prev); BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, nullptr); - /* We know there is only one loop in this space, - * no need to create a linklist in this case... */ + /* We know there is only one loop in this space, no need to create a link-list in this case. */ BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true); if (clnors_data) { @@ -1135,24 +1082,24 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli BLI_Stack *edge_vectors = data->edge_vectors; - /* Gah... We have to fan around current vertex, until we find the other non-smooth edge, + /* Sigh! we have to fan around current vertex, until we find the other non-smooth edge, * and accumulate face normals into the vertex! * Note in case this vertex has only one sharp edges, this is a waste because the normal is the * same as the vertex normal, but I do not see any easy way to detect that (would need to count * number of sharp edges per vertex, I doubt the additional memory usage would be worth it, - * especially as it should not be a common case in real-life meshes anyway). - */ + * especially as it should not be a common case in real-life meshes anyway). */ const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const MVert *mv_pivot = &mverts[mv_pivot_index]; - /* ml_curr would be mlfan_prev if we needed that one. */ + /* `ml_curr` would be mlfan_prev if we needed that one. */ const MEdge *me_org = &medges[ml_curr->e]; const int *e2lfan_curr; float vec_curr[3], vec_prev[3], vec_org[3]; const MLoop *mlfan_curr; float lnor[3] = {0.0f, 0.0f, 0.0f}; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex! + */ int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; /* We validate clnors data on the fly - cheapest way to do! */ @@ -1189,14 +1136,14 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli } } - // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); + // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); while (true) { const MEdge *me_curr = &medges[mlfan_curr->e]; /* Compute edge vectors. * NOTE: We could pre-compute those into an array, in the first iteration, instead of computing * them twice (or more) here. However, time gained is not worth memory and time lost, - * given the fact that this code should not be called that much in real-life meshes... + * given the fact that this code should not be called that much in real-life meshes. */ { const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] : @@ -1206,7 +1153,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli normalize_v3(vec_curr); } - // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); + // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); { /* Code similar to accumulate_vertex_normals_poly_v3. */ @@ -1246,9 +1193,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) { /* Current edge is sharp and we have finished with this fan of faces around this vert, - * or this vert is smooth, and we have completed a full turn around it. - */ - // printf("FAN: Finished!\n"); + * or this vert is smooth, and we have completed a full turn around it. */ + // printf("FAN: Finished!\n"); break; } @@ -1389,12 +1335,13 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ const int *e2lfan_curr; const MLoop *mlfan_curr; - /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex! + */ int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; e2lfan_curr = e2l_prev; if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop, so not a cyclic smooth fan... */ + /* Sharp loop, so not a cyclic smooth fan. */ return false; } @@ -1425,21 +1372,21 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops, e2lfan_curr = edge_to_loops[mlfan_curr->e]; if (IS_EDGE_SHARP(e2lfan_curr)) { - /* Sharp loop/edge, so not a cyclic smooth fan... */ + /* Sharp loop/edge, so not a cyclic smooth fan. */ return false; } - /* Smooth loop/edge... */ + /* Smooth loop/edge. */ if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) { if (mlfan_vert_index == ml_curr_index) { /* We walked around a whole cyclic smooth fan without finding any already-processed loop, - * means we can use initial ml_curr/ml_prev edge as start for this smooth fan. */ + * means we can use initial `ml_curr` / `ml_prev` edge as start for this smooth fan. */ return true; } - /* ... already checked in some previous looping, we can abort. */ + /* Already checked in some previous looping, we can abort. */ return false; } - /* ... we can skip it in future, and keep checking the smooth fan. */ + /* We can skip it in future, and keep checking the smooth fan. */ BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); } } @@ -1501,7 +1448,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common const int *e2l_prev = edge_to_loops[ml_prev->e]; #if 0 - printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...", + printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)", ml_curr_index, ml_curr->e, ml_curr->v, @@ -1531,12 +1478,12 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common ml_curr_index, ml_prev_index, mp_index))) { - // printf("SKIPPING!\n"); + // printf("SKIPPING!\n"); } else { LoopSplitTaskData *data, data_local; - // printf("PROCESSING!\n"); + // printf("PROCESSING!\n"); if (pool) { if (data_idx == 0) { @@ -1605,7 +1552,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common } } - /* Last block of data... Since it is calloc'ed and we use first nullptr item as stopper, + /* Last block of data. Since it is calloc'ed and we use first nullptr item as stopper, * everything is fine. */ if (pool && data_idx) { BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr); @@ -1652,8 +1599,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, * since we may want to use lnors even when mesh's 'autosmooth' is disabled * (see e.g. mesh mapping code). * As usual, we could handle that on case-by-case basis, - * but simpler to keep it well confined here. - */ + * but simpler to keep it well confined here. */ int mp_index; for (mp_index = 0; mp_index < numPolys; mp_index++) { @@ -1736,7 +1682,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false); if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { - /* Not enough loops to be worth the whole threading overhead... */ + /* Not enough loops to be worth the whole threading overhead. */ loop_split_generator(nullptr, &common_data); } else { @@ -1791,13 +1737,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, short (*r_clnors_data)[2], const bool use_vertices) { - /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling + /* We *may* make that poor #BKE_mesh_normals_loop_split() even more complex by making it handling * that feature too, would probably be more efficient in absolute. * However, this function *is not* performance-critical, since it is mostly expected to be called - * by io addons when importing custom normals, and modifier + * by io add-ons when importing custom normals, and modifier * (and perhaps from some editing tools later?). - * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice! - */ + * So better to keep some simplicity here, and just call #BKE_mesh_normals_loop_split() twice! */ MLoopNorSpaceArray lnors_spacearr = {nullptr}; BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__); float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__); @@ -1849,15 +1794,13 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, * This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans * matching given custom lnors. * Note this code *will never* unsharp edges! And quite obviously, - * when we set custom normals per vertices, running this is absolutely useless. - */ + * when we set custom normals per vertices, running this is absolutely useless. */ if (!use_vertices) { for (int i = 0; i < numLoops; i++) { if (!lnors_spacearr.lspacearr[i]) { /* This should not happen in theory, but in some rare case (probably ugly geometry) * we can get some nullptr loopspacearr at this point. :/ - * Maybe we should set those loops' edges as sharp? - */ + * Maybe we should set those loops' edges as sharp? */ BLI_BITMAP_ENABLE(done_loops, i); if (G.debug & G_DEBUG) { printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i); @@ -1867,12 +1810,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, if (!BLI_BITMAP_TEST(done_loops, i)) { /* Notes: - * * In case of mono-loop smooth fan, we have nothing to do. - * * Loops in this linklist are ordered (in reversed order compared to how they were + * - In case of mono-loop smooth fan, we have nothing to do. + * - Loops in this linklist are ordered (in reversed order compared to how they were * discovered by BKE_mesh_normals_loop_split(), but this is not a problem). * Which means if we find a mismatching clnor, * we know all remaining loops will have to be in a new, different smooth fan/lnor space. - * * In smooth fan case, we compare each clnor against a ref one, + * - In smooth fan case, we compare each clnor against a ref one, * to avoid small differences adding up into a real big one in the end! */ if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { @@ -1897,8 +1840,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, /* Current normal differs too much from org one, we have to tag the edge between * previous loop's face and current's one as sharp. * We know those two loops do not point to the same edge, - * since we do not allow reversed winding in a same smooth fan. - */ + * since we do not allow reversed winding in a same smooth fan. */ const MPoly *mp = &mpolys[loop_to_poly[lidx]]; const MLoop *mlp = &mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1]; @@ -1970,8 +1912,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts, if (BLI_BITMAP_TEST_BOOL(done_loops, i)) { /* Note we accumulate and average all custom normals in current smooth fan, * to avoid getting different clnors data (tiny differences in plain custom normals can - * give rather huge differences in computed 2D factors). - */ + * give rather huge differences in computed 2D factors). */ LinkNode *loops = lnors_spacearr.lspacearr[i]->loops; if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { BLI_assert(POINTER_AS_INT(loops) == i); @@ -2087,15 +2028,14 @@ static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const bool free_polynors = false; if (polynors == nullptr) { polynors = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (size_t)mesh->totpoly, __func__); - BKE_mesh_calc_normals_poly(mesh->mvert, - nullptr, - mesh->totvert, - mesh->mloop, - mesh->mpoly, - mesh->totloop, - mesh->totpoly, - polynors, - false); + BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert, + mesh->totvert, + mesh->mloop, + mesh->totloop, + mesh->mpoly, + mesh->totpoly, + polynors, + nullptr); free_polynors = true; } diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index c5e8858ea12..53a31cbbc7a 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -1379,14 +1379,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, } if (dirty_nors_dst || do_poly_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, numverts_dst, loops_dst, - polys_dst, numloops_dst, + polys_dst, numpolys_dst, - poly_nors_dst, - true); + poly_nors_dst); } } if (need_lnors_dst) { @@ -2231,14 +2229,12 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode, } if (dirty_nors_dst) { BKE_mesh_calc_normals_poly(verts_dst, - NULL, numverts_dst, loops_dst, - polys_dst, numloops_dst, + polys_dst, numpolys_dst, - poly_nors_dst, - true); + poly_nors_dst); } } diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 8364b0ec024..9f5703a015d 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -33,6 +33,7 @@ #include "BLI_array.hh" #include "BLI_float3.hh" #include "BLI_index_range.hh" +#include "BLI_span.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -48,7 +49,9 @@ #include "bmesh_tools.h" #ifdef WITH_OPENVDB -# include "openvdb_capi.h" +# include <openvdb/openvdb.h> +# include <openvdb/tools/MeshToVolume.h> +# include <openvdb/tools/VolumeToMesh.h> #endif #ifdef WITH_QUADRIFLOW @@ -58,6 +61,8 @@ using blender::Array; using blender::float3; using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; #ifdef WITH_QUADRIFLOW static Mesh *remesh_quadriflow(const Mesh *input_mesh, @@ -192,91 +197,80 @@ Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh, } #ifdef WITH_OPENVDB -static struct OpenVDBLevelSet *remesh_voxel_level_set_create(const Mesh *mesh, - struct OpenVDBTransform *transform) +static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh, + const float voxel_size) { - const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh); - MVertTri *verttri = (MVertTri *)MEM_callocN( - sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh), "remesh_looptri"); - BKE_mesh_runtime_verttri_from_looptri( - verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh)); + Span<MLoop> mloop{mesh->mloop, mesh->totloop}; + Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh), + BKE_mesh_runtime_looptri_len(mesh)}; - const int totfaces = BKE_mesh_runtime_looptri_len(mesh); - const int totverts = mesh->totvert; - Array<float3> verts(totverts); - Array<int> faces(totfaces * 3); + std::vector<openvdb::Vec3s> points(mesh->totvert); + std::vector<openvdb::Vec3I> triangles(looptris.size()); - for (const int i : IndexRange(totverts)) { - verts[i] = mesh->mvert[i].co; + for (const int i : IndexRange(mesh->totvert)) { + const float3 co = mesh->mvert[i].co; + points[i] = openvdb::Vec3s(co.x, co.y, co.z); } - for (const int i : IndexRange(totfaces)) { - MVertTri &vt = verttri[i]; - faces[i * 3] = vt.tri[0]; - faces[i * 3 + 1] = vt.tri[1]; - faces[i * 3 + 2] = vt.tri[2]; + for (const int i : IndexRange(looptris.size())) { + const MLoopTri &loop_tri = looptris[i]; + triangles[i] = openvdb::Vec3I( + mloop[loop_tri.tri[0]].v, mloop[loop_tri.tri[1]].v, mloop[loop_tri.tri[2]].v); } - struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, nullptr); - OpenVDBLevelSet_mesh_to_level_set( - level_set, (const float *)verts.data(), faces.data(), totverts, totfaces, transform); - - MEM_freeN(verttri); + openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform( + voxel_size); + openvdb::FloatGrid::Ptr grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>( + *transform, points, triangles, 1.0f); - return level_set; + return grid; } -static Mesh *remesh_voxel_volume_to_mesh(struct OpenVDBLevelSet *level_set, - double isovalue, - double adaptivity, - bool relax_disoriented_triangles) +static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set_grid, + const float isovalue, + const float adaptivity, + const bool relax_disoriented_triangles) { - struct OpenVDBVolumeToMeshData output_mesh; - OpenVDBLevelSet_volume_to_mesh( - level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles); - - Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices, - 0, - 0, - (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3), - output_mesh.totquads + output_mesh.tottriangles); - - for (const int i : IndexRange(output_mesh.totvertices)) { - copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]); + std::vector<openvdb::Vec3s> vertices; + std::vector<openvdb::Vec4I> quads; + std::vector<openvdb::Vec3I> tris; + openvdb::tools::volumeToMesh<openvdb::FloatGrid>( + *level_set_grid, vertices, tris, quads, isovalue, adaptivity, relax_disoriented_triangles); + + Mesh *mesh = BKE_mesh_new_nomain( + vertices.size(), 0, 0, quads.size() * 4 + tris.size() * 3, quads.size() + tris.size()); + MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> mloops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> mpolys{mesh->mpoly, mesh->totpoly}; + + for (const int i : mverts.index_range()) { + copy_v3_v3(mverts[i].co, float3(vertices[i].x(), vertices[i].y(), vertices[i].z())); } - for (const int i : IndexRange(output_mesh.totquads)) { - MPoly &poly = mesh->mpoly[i]; + for (const int i : IndexRange(quads.size())) { + MPoly &poly = mpolys[i]; const int loopstart = i * 4; poly.loopstart = loopstart; poly.totloop = 4; - mesh->mloop[loopstart].v = output_mesh.quads[loopstart]; - mesh->mloop[loopstart + 1].v = output_mesh.quads[loopstart + 1]; - mesh->mloop[loopstart + 2].v = output_mesh.quads[loopstart + 2]; - mesh->mloop[loopstart + 3].v = output_mesh.quads[loopstart + 3]; + mloops[loopstart].v = quads[i][0]; + mloops[loopstart + 1].v = quads[i][3]; + mloops[loopstart + 2].v = quads[i][2]; + mloops[loopstart + 3].v = quads[i][1]; } - const int triangle_poly_start = output_mesh.totquads; - const int triangle_loop_start = output_mesh.totquads * 4; - for (const int i : IndexRange(output_mesh.tottriangles)) { - MPoly &poly = mesh->mpoly[triangle_poly_start + i]; + const int triangle_loop_start = quads.size() * 4; + for (const int i : IndexRange(tris.size())) { + MPoly &poly = mpolys[quads.size() + i]; const int loopstart = triangle_loop_start + i * 3; poly.loopstart = loopstart; poly.totloop = 3; - mesh->mloop[loopstart].v = output_mesh.triangles[i * 3 + 2]; - mesh->mloop[loopstart + 1].v = output_mesh.triangles[i * 3 + 1]; - mesh->mloop[loopstart + 2].v = output_mesh.triangles[i * 3]; + mloops[loopstart].v = tris[i][2]; + mloops[loopstart + 1].v = tris[i][1]; + mloops[loopstart + 2].v = tris[i][0]; } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); - - MEM_freeN(output_mesh.quads); - MEM_freeN(output_mesh.vertices); - - if (output_mesh.tottriangles > 0) { - MEM_freeN(output_mesh.triangles); - } + BKE_mesh_normals_tag_dirty(mesh); return mesh; } @@ -288,14 +282,8 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, const float isovalue) { #ifdef WITH_OPENVDB - struct OpenVDBTransform *xform = OpenVDBTransform_create(); - OpenVDBTransform_create_linear_transform(xform, (double)voxel_size); - struct OpenVDBLevelSet *level_set = remesh_voxel_level_set_create(mesh, xform); - Mesh *new_mesh = remesh_voxel_volume_to_mesh( - level_set, (double)isovalue, (double)adaptivity, false); - OpenVDBLevelSet_free(level_set); - OpenVDBTransform_free(xform); - return new_mesh; + openvdb::FloatGrid::Ptr level_set = remesh_voxel_level_set_create(mesh, voxel_size); + return remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false); #else UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue); return nullptr; @@ -304,7 +292,7 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); @@ -342,7 +330,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source) void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY); const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT); @@ -389,7 +377,7 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source) void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) { - BVHTreeFromMesh bvhtree = {{nullptr}}; + BVHTreeFromMesh bvhtree = {nullptr}; BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 2088c4268e6..821ca7b98b3 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -249,11 +249,11 @@ bool BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md) return false; } -bool BKE_modifier_depends_ontime(ModifierData *md) +bool BKE_modifier_depends_ontime(Scene *scene, ModifierData *md, const int dag_eval_mode) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - return mti->dependsOnTime && mti->dependsOnTime(md); + return mti->dependsOnTime && mti->dependsOnTime(scene, md, dag_eval_mode); } bool BKE_modifier_supports_mapping(ModifierData *md) diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index f32b0c434c1..e507252307b 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -206,36 +206,35 @@ static void write_movieReconstruction(BlendWriter *writer, static void movieclip_blend_write(BlendWriter *writer, ID *id, const void *id_address) { MovieClip *clip = (MovieClip *)id; - if (clip->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - clip->anim = NULL; - clip->tracking_context = NULL; - clip->tracking.stats = NULL; - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + clip->anim = NULL; + clip->tracking_context = NULL; + clip->tracking.stats = NULL; - BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); - BKE_id_blend_write(writer, &clip->id); + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; - if (clip->adt) { - BKE_animdata_blend_write(writer, clip->adt); - } + BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); + BKE_id_blend_write(writer, &clip->id); + + if (clip->adt) { + BKE_animdata_blend_write(writer, clip->adt); + } - write_movieTracks(writer, &tracking->tracks); - write_moviePlaneTracks(writer, &tracking->plane_tracks); - write_movieReconstruction(writer, &tracking->reconstruction); + write_movieTracks(writer, &tracking->tracks); + write_moviePlaneTracks(writer, &tracking->plane_tracks); + write_movieReconstruction(writer, &tracking->reconstruction); - object = tracking->objects.first; - while (object) { - BLO_write_struct(writer, MovieTrackingObject, object); + object = tracking->objects.first; + while (object) { + BLO_write_struct(writer, MovieTrackingObject, object); - write_movieTracks(writer, &object->tracks); - write_moviePlaneTracks(writer, &object->plane_tracks); - write_movieReconstruction(writer, &object->reconstruction); + write_movieTracks(writer, &object->tracks); + write_moviePlaneTracks(writer, &object->plane_tracks); + write_movieReconstruction(writer, &object->reconstruction); - object = object->next; - } + object = object->next; } } diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 54f0da30a2b..eaa11a6683a 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -468,15 +468,9 @@ void multires_force_sculpt_rebuild(Object *object) object->sculpt->pbvh = NULL; } - if (ss->pmap != NULL) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } + MEM_SAFE_FREE(ss->pmap); - if (ss->pmap_mem != NULL) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } + MEM_SAFE_FREE(ss->pmap_mem); } void multires_force_external_reload(Object *object) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 9888e23a7bd..2a0e05a2616 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -606,19 +606,18 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bNodeTree *ntree = (bNodeTree *)id; - if (ntree->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - ntree->init = 0; /* to set callbacks and force setting types */ - ntree->is_updating = false; - ntree->typeinfo = nullptr; - ntree->interface_type = nullptr; - ntree->progress = nullptr; - ntree->execdata = nullptr; - BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ntree->init = 0; /* to set callbacks and force setting types */ + ntree->is_updating = false; + ntree->typeinfo = nullptr; + ntree->interface_type = nullptr; + ntree->progress = nullptr; + ntree->execdata = nullptr; - ntreeBlendWrite(writer, ntree); - } + BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); + + ntreeBlendWrite(writer, ntree); } static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) @@ -2364,7 +2363,7 @@ bNodeLink *nodeAddLink( ntree->update |= NTREE_UPDATE_LINKS; } - if (link->tosock->flag & SOCK_MULTI_INPUT) { + if (link != nullptr && link->tosock->flag & SOCK_MULTI_INPUT) { link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1; } @@ -3194,6 +3193,7 @@ void ntreeFreeEmbeddedTree(bNodeTree *ntree) { ntreeFreeTree(ntree); BKE_libblock_free_data(&ntree->id, true); + BKE_libblock_free_data_py(&ntree->id); } void ntreeFreeLocalTree(bNodeTree *ntree) @@ -5149,6 +5149,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); register_node_type_geo_curve_set_handles(); + register_node_type_geo_curve_spline_type(); register_node_type_geo_curve_subdivide(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); @@ -5180,6 +5181,7 @@ static void registerGeometryNodes() register_node_type_geo_points_to_volume(); register_node_type_geo_raycast(); register_node_type_geo_sample_texture(); + register_node_type_geo_select_by_handle_type(); register_node_type_geo_select_by_material(); register_node_type_geo_separate_components(); register_node_type_geo_subdivision_surface(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 89de37d6e4b..1c08a46adc3 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -144,6 +144,7 @@ #include "DRW_engine.h" #include "BLO_read_write.h" +#include "BLO_readfile.h" #include "SEQ_sequencer.h" @@ -522,74 +523,72 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre Object *ob = (Object *)id; const bool is_undo = BLO_write_is_undo(writer); - if (ob->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ - BKE_object_runtime_reset(ob); - if (is_undo) { - /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as - * well, can help reducing false detection of changed data-blocks. */ - ob->mode &= ~OB_MODE_EDIT; - } + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + BKE_object_runtime_reset(ob); - /* write LibData */ - BLO_write_id_struct(writer, Object, id_address, &ob->id); - BKE_id_blend_write(writer, &ob->id); + if (is_undo) { + /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as + * well, can help reducing false detection of changed data-blocks. */ + ob->mode &= ~OB_MODE_EDIT; + } - if (ob->adt) { - BKE_animdata_blend_write(writer, ob->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Object, id_address, &ob->id); + BKE_id_blend_write(writer, &ob->id); + + if (ob->adt) { + BKE_animdata_blend_write(writer, ob->adt); + } - /* direct data */ - BLO_write_pointer_array(writer, ob->totcol, ob->mat); - BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); + /* direct data */ + BLO_write_pointer_array(writer, ob->totcol, ob->mat); + BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits); - bArmature *arm = NULL; - if (ob->type == OB_ARMATURE) { - arm = ob->data; - if (arm && ob->pose && arm->act_bone) { - BLI_strncpy( - ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); - } + bArmature *arm = NULL; + if (ob->type == OB_ARMATURE) { + arm = ob->data; + if (arm && ob->pose && arm->act_bone) { + BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone)); } + } - BKE_pose_blend_write(writer, ob->pose, arm); - write_fmaps(writer, &ob->fmaps); - BKE_constraint_blend_write(writer, &ob->constraints); - animviz_motionpath_blend_write(writer, ob->mpath); + BKE_pose_blend_write(writer, ob->pose, arm); + write_fmaps(writer, &ob->fmaps); + BKE_constraint_blend_write(writer, &ob->constraints); + animviz_motionpath_blend_write(writer, ob->mpath); - BLO_write_struct(writer, PartDeflect, ob->pd); - if (ob->soft) { - /* Set deprecated pointers to prevent crashes of older Blenders */ - ob->soft->pointcache = ob->soft->shared->pointcache; - ob->soft->ptcaches = ob->soft->shared->ptcaches; - BLO_write_struct(writer, SoftBody, ob->soft); - BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); - BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); - BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); - } + BLO_write_struct(writer, PartDeflect, ob->pd); + if (ob->soft) { + /* Set deprecated pointers to prevent crashes of older Blenders */ + ob->soft->pointcache = ob->soft->shared->pointcache; + ob->soft->ptcaches = ob->soft->shared->ptcaches; + BLO_write_struct(writer, SoftBody, ob->soft); + BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); + BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches)); + BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); + } - if (ob->rigidbody_object) { - /* TODO: if any extra data is added to handle duplis, will need separate function then */ - BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); - } - if (ob->rigidbody_constraint) { - BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); - } + if (ob->rigidbody_object) { + /* TODO: if any extra data is added to handle duplis, will need separate function then */ + BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object); + } + if (ob->rigidbody_constraint) { + BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint); + } - if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { - BLO_write_struct(writer, ImageUser, ob->iuser); - } + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { + BLO_write_struct(writer, ImageUser, ob->iuser); + } - BKE_particle_system_blend_write(writer, &ob->particlesystem); - BKE_modifier_blend_write(writer, &ob->modifiers); - BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); - BKE_shaderfx_blend_write(writer, &ob->shader_fx); + BKE_particle_system_blend_write(writer, &ob->particlesystem); + BKE_modifier_blend_write(writer, &ob->modifiers); + BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers); + BKE_shaderfx_blend_write(writer, &ob->shader_fx); - BLO_write_struct_list(writer, LinkData, &ob->pc_ids); + BLO_write_struct_list(writer, LinkData, &ob->pc_ids); - BKE_previewimg_blend_write(writer, ob->preview); - } + BKE_previewimg_blend_write(writer, ob->preview); } /* XXX deprecated - old animation system */ @@ -833,7 +832,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) { Object *ob = (Object *)id; - bool warn = false; + BlendFileReadReport *reports = BLO_read_lib_reports(reader); /* XXX deprecated - old animation system <<< */ BLO_read_id_address(reader, ob->id.lib, &ob->ipo); @@ -851,8 +850,8 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) else { if (ob->instance_collection != NULL) { ID *new_id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id); - BLO_reportf_wrap(BLO_read_lib_reports(reader), - RPT_WARNING, + BLO_reportf_wrap(reports, + RPT_INFO, TIP_("Non-Empty object '%s' cannot duplicate collection '%s' " "anymore in Blender 2.80, removed instancing"), ob->id.name + 2, @@ -870,11 +869,17 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) ob->proxy = NULL; if (ob->id.lib) { - printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Proxy lost from object %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); } else { - printf("Proxy lost from object %s lib <NONE>\n", ob->id.name + 2); + BLO_reportf_wrap( + reports, RPT_INFO, TIP_("Proxy lost from object %s lib <NONE>\n"), ob->id.name + 2); } + reports->count.missing_obproxies++; } else { /* this triggers object_update to always use a copy */ @@ -887,15 +892,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, ob->id.lib, &ob->data); if (ob->data == NULL && poin != NULL) { - if (ob->id.lib) { - printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath); - } - else { - printf("Object %s lost data.\n", ob->id.name + 2); - } - ob->type = OB_EMPTY; - warn = true; if (ob->pose) { /* we can't call #BKE_pose_free() here because of library linking @@ -911,6 +908,18 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) ob->pose = NULL; ob->mode &= ~OB_MODE_POSE; } + + if (ob->id.lib) { + BLO_reportf_wrap(reports, + RPT_INFO, + TIP_("Can't find object data of %s lib %s\n"), + ob->id.name + 2, + ob->id.lib->filepath); + } + else { + BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2); + } + reports->count.missing_obdata++; } for (int a = 0; a < ob->totcol; a++) { BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); @@ -922,7 +931,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) const short *totcol_data = BKE_object_material_len_p(ob); /* Only expand so as not to lose any object materials that might be set. */ if (totcol_data && (*totcol_data > ob->totcol)) { - /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */ + // printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false); } } @@ -992,10 +1001,6 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1); BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); } - - if (warn) { - BLO_reportf_wrap(BLO_read_lib_reports(reader), RPT_WARNING, "Warning in console"); - } } /* XXX deprecated - old animation system */ @@ -2074,6 +2079,12 @@ static void object_init(Object *ob, const short ob_type) if (ob->type == OB_GPENCIL) { ob->dtx |= OB_USE_GPENCIL_LIGHTS; } + + if (ob->type == OB_LAMP) { + /* Lights are invisible to camera rays and are assumed to be a + * shadow catcher by default. */ + ob->visibility_flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; + } } void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) @@ -4034,10 +4045,7 @@ void BKE_object_empty_draw_type_set(Object *ob, const int value) } } else { - if (ob->iuser) { - MEM_freeN(ob->iuser); - ob->iuser = NULL; - } + MEM_SAFE_FREE(ob->iuser); } } @@ -5401,9 +5409,12 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) return tree; } -bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) +bool BKE_object_modifier_use_time(Scene *scene, + Object *ob, + ModifierData *md, + const int dag_eval_mode) { - if (BKE_modifier_depends_ontime(md)) { + if (BKE_modifier_depends_ontime(scene, md, dag_eval_mode)) { return true; } diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index c69326a23c6..511f5d4ae66 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -307,10 +307,7 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); - if (lt->dvert) { - MEM_freeN(lt->dvert); - lt->dvert = NULL; - } + MEM_SAFE_FREE(lt->dvert); } } else if (BKE_object_defgroup_active_index_get(ob) < 1) { @@ -465,10 +462,7 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) } else if (ob->type == OB_LATTICE) { Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); - if (lt->dvert) { - MEM_freeN(lt->dvert); - lt->dvert = NULL; - } + MEM_SAFE_FREE(lt->dvert); } /* Fix counters/indices */ BKE_object_defgroup_active_index_set(ob, 0); diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 77969328365..141a9a25eca 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -1554,15 +1554,15 @@ static const DupliGenerator gen_dupli_particles = { static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) { int transflag = ctx->object->transflag; - int restrictflag = ctx->object->restrictflag; + int visibility_flag = ctx->object->visibility_flag; if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == nullptr) { return nullptr; } /* Should the dupli's be generated for this object? - Respect restrict flags. */ - if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) : - (restrictflag & OB_RESTRICT_VIEWPORT)) { + if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (visibility_flag & OB_HIDE_RENDER) : + (visibility_flag & OB_HIDE_VIEWPORT)) { return nullptr; } diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 3aee5cd639d..e9683d3b52c 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -530,7 +530,7 @@ static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -563,7 +563,7 @@ static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -596,7 +596,7 @@ static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void *UNUSED(t for (j = 0; j <= o->_N / 2; j++) { fftw_complex mul_param; - /* init_complex(mul_param, -scale, 0); */ + // init_complex(mul_param, -scale, 0); init_complex(mul_param, -1, 0); mul_complex_f(mul_param, mul_param, chop_amount); @@ -650,6 +650,14 @@ static void ocean_compute_normal_z(TaskPool *__restrict pool, void *UNUSED(taskd fftw_execute(o->_N_z_plan); } +/** + * Return true if the ocean is valid and can be used. + */ +bool BKE_ocean_is_valid(const struct Ocean *o) +{ + return o->_k != NULL; +} + void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount) { TaskPool *pool; @@ -769,7 +777,10 @@ bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution) return true; } -void BKE_ocean_init_from_modifier(struct Ocean *ocean, +/** + * Return true if the ocean data is valid and can be used. + */ +bool BKE_ocean_init_from_modifier(struct Ocean *ocean, struct OceanModifierData const *omd, const int resolution) { @@ -783,31 +794,34 @@ void BKE_ocean_init_from_modifier(struct Ocean *ocean, BKE_ocean_free_data(ocean); - BKE_ocean_init(ocean, - resolution * resolution, - resolution * resolution, - omd->spatial_size, - omd->spatial_size, - omd->wind_velocity, - omd->smallest_wave, - 1.0, - omd->wave_direction, - omd->damp, - omd->wave_alignment, - omd->depth, - omd->time, - omd->spectrum, - omd->fetch_jonswap, - omd->sharpen_peak_jonswap, - do_heightfield, - do_chop, - do_spray, - do_normals, - do_jacobian, - omd->seed); -} - -void BKE_ocean_init(struct Ocean *o, + return BKE_ocean_init(ocean, + resolution * resolution, + resolution * resolution, + omd->spatial_size, + omd->spatial_size, + omd->wind_velocity, + omd->smallest_wave, + 1.0, + omd->wave_direction, + omd->damp, + omd->wave_alignment, + omd->depth, + omd->time, + omd->spectrum, + omd->fetch_jonswap, + omd->sharpen_peak_jonswap, + do_heightfield, + do_chop, + do_spray, + do_normals, + do_jacobian, + omd->seed); +} + +/** + * Return true if the ocean data is valid and can be used. + */ +bool BKE_ocean_init(struct Ocean *o, int M, int N, float Lx, @@ -830,7 +844,6 @@ void BKE_ocean_init(struct Ocean *o, short do_jacobian, int seed) { - RNG *rng; int i, j, ii; BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE); @@ -858,18 +871,34 @@ void BKE_ocean_init(struct Ocean *o, o->_fetch_jonswap = fetch_jonswap; o->_sharpen_peak_jonswap = sharpen_peak_jonswap * 10.0f; + /* NOTE: most modifiers don't account for failure to allocate. + * In this case however a large resolution can easily perform large allocations that fail, + * support early exiting in this case. */ + if ((o->_k = (float *)MEM_mallocN(sizeof(float) * (size_t)M * (1 + N / 2), "ocean_k")) && + (o->_h0 = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N, "ocean_h0")) && + (o->_h0_minus = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N, + "ocean_h0_minus")) && + (o->_kx = (float *)MEM_mallocN(sizeof(float) * o->_M, "ocean_kx")) && + (o->_kz = (float *)MEM_mallocN(sizeof(float) * o->_N, "ocean_kz"))) { + /* Success. */ + } + else { + MEM_SAFE_FREE(o->_k); + MEM_SAFE_FREE(o->_h0); + MEM_SAFE_FREE(o->_h0_minus); + MEM_SAFE_FREE(o->_kx); + MEM_SAFE_FREE(o->_kz); + + BLI_rw_mutex_unlock(&o->oceanmutex); + return false; + } + o->_do_disp_y = do_height_field; o->_do_normals = do_normals; o->_do_spray = do_spray; o->_do_chop = do_chop; o->_do_jacobian = do_jacobian; - o->_k = (float *)MEM_mallocN(M * (1 + N / 2) * sizeof(float), "ocean_k"); - o->_h0 = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0"); - o->_h0_minus = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0_minus"); - o->_kx = (float *)MEM_mallocN(o->_M * sizeof(float), "ocean_kx"); - o->_kz = (float *)MEM_mallocN(o->_N * sizeof(float), "ocean_kz"); - /* make this robust in the face of erroneous usage */ if (o->_Lx == 0.0f) { o->_Lx = 0.001f; @@ -902,11 +931,11 @@ void BKE_ocean_init(struct Ocean *o, /* pre-calculate the k matrix */ for (i = 0; i < o->_M; i++) { for (j = 0; j <= o->_N / 2; j++) { - o->_k[i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]); + o->_k[(size_t)i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]); } } - rng = BLI_rng_new(seed); + RNG *rng = BLI_rng_new(seed); for (i = 0; i < o->_M; i++) { for (j = 0; j < o->_N; j++) { @@ -986,7 +1015,7 @@ void BKE_ocean_init(struct Ocean *o, "ocean_fft_in_nz"); o->_N_x = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_x"); - /* o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); (MEM01) */ + // o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); /* (MEM01) */ o->_N_z = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_z"); o->_N_x_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_nx, o->_N_x, FFTW_ESTIMATE); @@ -1029,6 +1058,8 @@ void BKE_ocean_init(struct Ocean *o, set_height_normalize_factor(o); BLI_rng_free(rng); + + return true; } void BKE_ocean_free_data(struct Ocean *oc) @@ -1052,7 +1083,7 @@ void BKE_ocean_free_data(struct Ocean *oc) fftw_destroy_plan(oc->_N_x_plan); fftw_destroy_plan(oc->_N_z_plan); MEM_freeN(oc->_N_x); - /* fftwf_free(oc->_N_y); (MEM01) */ + // fftwf_free(oc->_N_y); /* (MEM01) */ MEM_freeN(oc->_N_z); } @@ -1608,7 +1639,7 @@ struct Ocean *BKE_ocean_add(void) return oc; } -void BKE_ocean_init(struct Ocean *UNUSED(o), +bool BKE_ocean_init(struct Ocean *UNUSED(o), int UNUSED(M), int UNUSED(N), float UNUSED(Lx), @@ -1631,6 +1662,7 @@ void BKE_ocean_init(struct Ocean *UNUSED(o), short UNUSED(do_jacobian), int UNUSED(seed)) { + return false; } void BKE_ocean_free_data(struct Ocean *UNUSED(oc)) @@ -1700,10 +1732,11 @@ void BKE_ocean_bake(struct Ocean *UNUSED(o), (void)update_cb; } -void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), +bool BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean), struct OceanModifierData const *UNUSED(omd), int UNUSED(resolution)) { + return true; } #endif /* WITH_OCEANSIM */ diff --git a/source/blender/blenkernel/intern/ocean_spectrum.c b/source/blender/blenkernel/intern/ocean_spectrum.c index 7ed70234baf..c5504b22b43 100644 --- a/source/blender/blenkernel/intern/ocean_spectrum.c +++ b/source/blender/blenkernel/intern/ocean_spectrum.c @@ -77,7 +77,7 @@ static float ocean_spectrum_wind_and_damp(const Ocean *oc, float newval = val * pow(fabs(k_dot_w), oc->_wind_alignment); /* Eliminate wavelengths smaller than cutoff. */ - /* val *= exp(-k2 * m_cutoff); */ + // val *= exp(-k2 * m_cutoff); /* Reduce reflected waves. */ if (k_dot_w < 0.0f) { diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index a1fa6aae1ce..d6030941c6d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -108,14 +108,13 @@ static void palette_free_data(ID *id) static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Palette *palette = (Palette *)id; - if (palette->id.us > 0 || BLO_write_is_undo(writer)) { - PaletteColor *color; - BLO_write_id_struct(writer, Palette, id_address, &palette->id); - BKE_id_blend_write(writer, &palette->id); - for (color = palette->colors.first; color; color = color->next) { - BLO_write_struct(writer, PaletteColor, color); - } + PaletteColor *color; + BLO_write_id_struct(writer, Palette, id_address, &palette->id); + BKE_id_blend_write(writer, &palette->id); + + for (color = palette->colors.first; color; color = color->next) { + BLO_write_struct(writer, PaletteColor, color); } } @@ -187,12 +186,11 @@ static void paint_curve_free_data(ID *id) static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) { PaintCurve *pc = (PaintCurve *)id; - if (pc->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); - BKE_id_blend_write(writer, &pc->id); - BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); - } + BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); + BKE_id_blend_write(writer, &pc->id); + + BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); } static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index f2f3c5d4ca6..29849c69b6f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -255,60 +255,59 @@ static void write_boid_state(BlendWriter *writer, BoidState *state) static void particle_settings_blend_write(BlendWriter *writer, ID *id, const void *id_address) { ParticleSettings *part = (ParticleSettings *)id; - if (part->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); - BKE_id_blend_write(writer, &part->id); - if (part->adt) { - BKE_animdata_blend_write(writer, part->adt); - } - BLO_write_struct(writer, PartDeflect, part->pd); - BLO_write_struct(writer, PartDeflect, part->pd2); - BLO_write_struct(writer, EffectorWeights, part->effector_weights); + /* write LibData */ + BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); + BKE_id_blend_write(writer, &part->id); - if (part->clumpcurve) { - BKE_curvemapping_blend_write(writer, part->clumpcurve); - } - if (part->roughcurve) { - BKE_curvemapping_blend_write(writer, part->roughcurve); - } - if (part->twistcurve) { - BKE_curvemapping_blend_write(writer, part->twistcurve); - } + if (part->adt) { + BKE_animdata_blend_write(writer, part->adt); + } + BLO_write_struct(writer, PartDeflect, part->pd); + BLO_write_struct(writer, PartDeflect, part->pd2); + BLO_write_struct(writer, EffectorWeights, part->effector_weights); - LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) { - /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */ - if (dw->ob != NULL) { - dw->index = 0; - if (part->instance_collection) { /* can be NULL if lining fails or set to None */ - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) { - if (object == dw->ob) { - break; - } - dw->index++; + if (part->clumpcurve) { + BKE_curvemapping_blend_write(writer, part->clumpcurve); + } + if (part->roughcurve) { + BKE_curvemapping_blend_write(writer, part->roughcurve); + } + if (part->twistcurve) { + BKE_curvemapping_blend_write(writer, part->twistcurve); + } + + LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) { + /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */ + if (dw->ob != NULL) { + dw->index = 0; + if (part->instance_collection) { /* can be NULL if lining fails or set to None */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) { + if (object == dw->ob) { + break; } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + dw->index++; } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - BLO_write_struct(writer, ParticleDupliWeight, dw); } + BLO_write_struct(writer, ParticleDupliWeight, dw); + } - if (part->boids && part->phystype == PART_PHYS_BOIDS) { - BLO_write_struct(writer, BoidSettings, part->boids); + if (part->boids && part->phystype == PART_PHYS_BOIDS) { + BLO_write_struct(writer, BoidSettings, part->boids); - LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { - write_boid_state(writer, state); - } - } - if (part->fluid && part->phystype == PART_PHYS_FLUID) { - BLO_write_struct(writer, SPHFluidSettings, part->fluid); + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + write_boid_state(writer, state); } + } + if (part->fluid && part->phystype == PART_PHYS_FLUID) { + BLO_write_struct(writer, SPHFluidSettings, part->fluid); + } - for (int a = 0; a < MAX_MTEX; a++) { - if (part->mtex[a]) { - BLO_write_struct(writer, MTex, part->mtex[a]); - } + for (int a = 0; a < MAX_MTEX; a++) { + if (part->mtex[a]) { + BLO_write_struct(writer, MTex, part->mtex[a]); } } } @@ -928,10 +927,7 @@ void free_hair(Object *object, ParticleSystem *psys, int dynamics) LOOP_PARTICLES { - if (pa->hair) { - MEM_freeN(pa->hair); - } - pa->hair = NULL; + MEM_SAFE_FREE(pa->hair); pa->totkey = 0; } @@ -1044,25 +1040,13 @@ void psys_free_particles(ParticleSystem *psys) void psys_free_pdd(ParticleSystem *psys) { if (psys->pdd) { - if (psys->pdd->cdata) { - MEM_freeN(psys->pdd->cdata); - } - psys->pdd->cdata = NULL; + MEM_SAFE_FREE(psys->pdd->cdata); - if (psys->pdd->vdata) { - MEM_freeN(psys->pdd->vdata); - } - psys->pdd->vdata = NULL; + MEM_SAFE_FREE(psys->pdd->vdata); - if (psys->pdd->ndata) { - MEM_freeN(psys->pdd->ndata); - } - psys->pdd->ndata = NULL; + MEM_SAFE_FREE(psys->pdd->ndata); - if (psys->pdd->vedata) { - MEM_freeN(psys->pdd->vedata); - } - psys->pdd->vedata = NULL; + MEM_SAFE_FREE(psys->pdd->vedata); psys->pdd->totpoint = 0; psys->pdd->totpart = 0; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 06d3daaf4d6..60edb78f8ba 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -169,10 +169,7 @@ void psys_reset(ParticleSystem *psys, int mode) } /* reset children */ - if (psys->child) { - MEM_freeN(psys->child); - psys->child = NULL; - } + MEM_SAFE_FREE(psys->child); psys->totchild = 0; @@ -182,10 +179,7 @@ void psys_reset(ParticleSystem *psys, int mode) /* reset point cache */ BKE_ptcache_invalidate(psys->pointcache); - if (psys->fluid_springs) { - MEM_freeN(psys->fluid_springs); - psys->fluid_springs = NULL; - } + MEM_SAFE_FREE(psys->fluid_springs); psys->tot_fluidsprings = psys->alloc_fluidsprings = 0; } @@ -3112,7 +3106,7 @@ static void collision_fail(ParticleData *pa, ParticleCollision *col) copy_v3_v3(pa->state.vel, col->pce.vel); mul_v3_fl(pa->state.vel, col->inv_timestep); - /* printf("max iterations\n"); */ + // printf("max iterations\n"); } /* Particle - Mesh collision detection and response @@ -4516,10 +4510,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ reset_all_particles(sim, 0.0, cfra, oldtotpart); free_unexisting_particles(sim); - if (psys->fluid_springs) { - MEM_freeN(psys->fluid_springs); - psys->fluid_springs = NULL; - } + MEM_SAFE_FREE(psys->fluid_springs); psys->tot_fluidsprings = psys->alloc_fluidsprings = 0; @@ -4770,6 +4761,7 @@ static void particle_settings_free_local(ParticleSettings *particle_settings) { BKE_libblock_free_datablock(&particle_settings->id, 0); BKE_libblock_free_data(&particle_settings->id, false); + BLI_assert(!particle_settings->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(particle_settings); } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 461ffa7765e..ca1fada8c76 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1026,7 +1026,7 @@ static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, PBVHNode *node = data->nodes[n]; float(*vnors)[3] = data->vnors; - if ((node->flag & PBVH_UpdateNormals)) { + if (node->flag & PBVH_UpdateNormals) { unsigned int mpoly_prev = UINT_MAX; float fn[3]; diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 9ed5b0230e6..57225872c7e 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2753,18 +2753,18 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra) pid->cache->flag |= PTCACHE_FLAG_INFO_DIRTY; } -int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) +bool BKE_ptcache_id_exist(PTCacheID *pid, int cfra) { if (!pid->cache) { - return 0; + return false; } if (cfra < pid->cache->startframe || cfra > pid->cache->endframe) { - return 0; + return false; } if (pid->cache->cached_frames && pid->cache->cached_frames[cfra - pid->cache->startframe] == 0) { - return 0; + return false; } if (pid->cache->flag & PTCACHE_DISK_CACHE) { @@ -2779,10 +2779,10 @@ int BKE_ptcache_id_exist(PTCacheID *pid, int cfra) for (; pm; pm = pm->next) { if (pm->frame == cfra) { - return 1; + return true; } } - return 0; + return false; } void BKE_ptcache_id_time( PTCacheID *pid, Scene *scene, float cfra, int *startframe, int *endframe, float *timescale) @@ -3369,7 +3369,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker) } /* NOTE: breaking baking should leave calculated frames in cache, not clear it */ - if ((cancel || G.is_break)) { + if (cancel || G.is_break) { break; } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index d9a7a376e2e..837a772607f 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -112,28 +112,27 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data) static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_address) { PointCloud *pointcloud = (PointCloud *)id; - if (pointcloud->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_blend_write_prepare( - &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - - /* Write LibData */ - BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); - BKE_id_blend_write(writer, &pointcloud->id); - - /* Direct data */ - CustomData_blend_write( - writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); - - BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); - if (pointcloud->adt) { - BKE_animdata_blend_write(writer, pointcloud->adt); - } - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } + CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_blend_write_prepare( + &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); + + /* Write LibData */ + BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); + BKE_id_blend_write(writer, &pointcloud->id); + + /* Direct data */ + CustomData_blend_write( + writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); + + BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); + if (pointcloud->adt) { + BKE_animdata_blend_write(writer, pointcloud->adt); + } + + /* Remove temporary data. */ + if (players && players != players_buff) { + MEM_freeN(players); } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3f75d0963c6..3fe00adc4d5 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -109,6 +109,8 @@ #include "RE_engine.h" +#include "RNA_access.h" + #include "SEQ_edit.h" #include "SEQ_iterator.h" #include "SEQ_modifier.h" @@ -445,7 +447,8 @@ static void scene_free_data(ID *id) * for objects directly in the master collection? then other * collections in the scene need to do it too? */ if (scene->master_collection) { - BKE_collection_free(scene->master_collection); + BKE_collection_free_data(scene->master_collection); + BKE_libblock_free_data_py(&scene->master_collection->id); MEM_freeN(scene->master_collection); scene->master_collection = NULL; } @@ -2178,7 +2181,7 @@ int BKE_scene_base_iter_next( /* exception: empty scene layer */ while ((*scene)->set) { (*scene) = (*scene)->set; - ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene); if (view_layer_set->object_bases.first) { *base = view_layer_set->object_bases.first; *ob = (*base)->object; @@ -2199,7 +2202,7 @@ int BKE_scene_base_iter_next( /* (*scene) is finished, now do the set */ while ((*scene)->set) { (*scene) = (*scene)->set; - ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene); if (view_layer_set->object_bases.first) { *base = view_layer_set->object_bases.first; *ob = (*base)->object; @@ -2305,7 +2308,7 @@ Object *BKE_scene_camera_switch_find(Scene *scene) Object *first_camera = NULL; LISTBASE_FOREACH (TimeMarker *, m, &scene->markers) { - if (m->camera && (m->camera->restrictflag & OB_RESTRICT_RENDER) == 0) { + if (m->camera && (m->camera->visibility_flag & OB_HIDE_RENDER) == 0) { if ((m->frame <= ctime) && (m->frame > frame)) { camera = m->camera; frame = m->frame; @@ -2898,7 +2901,7 @@ Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base) next_set: /* Reached the end, get the next base in the set. */ while ((*sce_iter = (*sce_iter)->set)) { - ViewLayer *view_layer_set = BKE_view_layer_default_render((*sce_iter)); + ViewLayer *view_layer_set = BKE_view_layer_default_render(*sce_iter); base = (Base *)view_layer_set->object_bases.first; if (base) { @@ -2937,6 +2940,22 @@ bool BKE_scene_uses_cycles(const Scene *scene) return STREQ(scene->r.engine, RE_engine_id_CYCLES); } +/* This enumeration has to match the one defined in the Cycles addon. */ +typedef enum eCyclesFeatureSet { + CYCLES_FEATURES_SUPPORTED = 0, + CYCLES_FEATURES_EXPERIMENTAL = 1, +} eCyclesFeatureSet; + +/* We cannot use const as RNA_id_pointer_create is not using a const ID. */ +bool BKE_scene_uses_cycles_experimental_features(Scene *scene) +{ + BLI_assert(BKE_scene_uses_cycles(scene)); + PointerRNA scene_ptr; + RNA_id_pointer_create(&scene->id, &scene_ptr); + PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); + return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL; +} + void BKE_scene_base_flag_to_objects(ViewLayer *view_layer) { Base *base = view_layer->object_bases.first; @@ -3118,7 +3137,7 @@ bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const Scene return false; } - if ((srv->viewflag & SCE_VIEW_DISABLE)) { + if (srv->viewflag & SCE_VIEW_DISABLE) { return false; } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index c3885b5dcf7..065240bddbc 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -255,18 +255,16 @@ static void screen_foreach_id(ID *id, LibraryForeachIDData *data) static void screen_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bScreen *screen = (bScreen *)id; - /* Screens are reference counted, only saved if used by a workspace. */ - if (screen->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ - BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen); - BKE_id_blend_write(writer, &screen->id); - BKE_previewimg_blend_write(writer, screen->preview); + /* write LibData */ + /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ + BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen); + BKE_id_blend_write(writer, &screen->id); - /* direct data */ - BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen)); - } + BKE_previewimg_blend_write(writer, screen->preview); + + /* direct data */ + BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen)); } /* Cannot use IDTypeInfo callback yet, because of the return value. */ @@ -730,7 +728,7 @@ void BKE_screen_area_map_free(ScrAreaMap *area_map) } /** Free (or release) any data used by this screen (does not free the screen itself). */ -void BKE_screen_free(bScreen *screen) +void BKE_screen_free_data(bScreen *screen) { screen_free_data(&screen->id); } @@ -766,7 +764,7 @@ void BKE_screen_remove_double_scrverts(bScreen *screen) while (v1) { if (v1->newv == NULL) { /* !?! */ if (v1->vec.x == verg->vec.x && v1->vec.y == verg->vec.y) { - /* printf("doublevert\n"); */ + // printf("doublevert\n"); v1->newv = verg; } } diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 216563b860d..4c97ccdf8b1 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -49,14 +49,10 @@ #include "BKE_simulation.h" #include "NOD_geometry.h" -#include "NOD_node_tree_multi_function.hh" #include "BLI_map.hh" #include "BLT_translation.h" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -114,19 +110,18 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Simulation *simulation = (Simulation *)id; - if (simulation->id.us > 0 || BLO_write_is_undo(writer)) { - BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); - BKE_id_blend_write(writer, &simulation->id); - - if (simulation->adt) { - BKE_animdata_blend_write(writer, simulation->adt); - } - - /* nodetree is integral part of simulation, no libdata */ - if (simulation->nodetree) { - BLO_write_struct(writer, bNodeTree, simulation->nodetree); - ntreeBlendWrite(writer, simulation->nodetree); - } + + BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); + BKE_id_blend_write(writer, &simulation->id); + + if (simulation->adt) { + BKE_animdata_blend_write(writer, simulation->adt); + } + + /* nodetree is integral part of simulation, no libdata */ + if (simulation->nodetree) { + BLO_write_struct(writer, bNodeTree, simulation->nodetree); + ntreeBlendWrite(writer, simulation->nodetree); } } diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index e4e2ed94b41..fbc781f5eb9 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -891,11 +891,7 @@ static void free_softbody_baked(SoftBody *sb) MEM_freeN(key); } } - if (sb->keys) { - MEM_freeN(sb->keys); - } - - sb->keys = NULL; + MEM_SAFE_FREE(sb->keys); sb->totkey = 0; } static void free_scratch(SoftBody *sb) @@ -2225,7 +2221,7 @@ static void sb_cf_threads_run(Scene *scene, totthread--; } - /* printf("sb_cf_threads_run spawning %d threads\n", totthread); */ + // printf("sb_cf_threads_run spawning %d threads\n", totthread); sb_threads = MEM_callocN(sizeof(SB_thread_context) * totthread, "SBThread"); memset(sb_threads, 0, sizeof(SB_thread_context) * totthread); @@ -2281,7 +2277,7 @@ static void softbody_calc_forces( float fieldfactor = -1.0f, windfactor = 0.25; int do_deflector /*, do_selfcollision */, do_springcollision, do_aero; - /* gravity = sb->grav * sb_grav_force_scale(ob); */ /* UNUSED */ + // gravity = sb->grav * sb_grav_force_scale(ob); /* UNUSED */ /* check conditions for various options */ do_deflector = query_external_colliders(depsgraph, sb->collision_group); @@ -2753,7 +2749,7 @@ static void mesh_to_softbody(Object *ob) build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */ /* insert *other second order* springs if desired */ if (sb->secondspring > 0.0000001f) { - /* exploits the first run of build_bps_springlist(ob); */ + /* Exploits the first run of `build_bps_springlist(ob)`. */ add_2nd_order_springs(ob, sb->secondspring); /* yes we need to do it again. */ build_bps_springlist(ob); @@ -2812,7 +2808,7 @@ static void reference_to_scratch(Object *ob) } mul_v3_fl(accu_pos, 1.0f / accu_mass); copy_v3_v3(sb->scratch->Ref.com, accu_pos); - /* printf("reference_to_scratch\n"); */ + // printf("reference_to_scratch\n"); } /* @@ -3447,10 +3443,10 @@ static void softbody_step( float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */ if (sb->scratch->flag & SBF_DOFUZZY) { - ///* stay with this stepsize unless err really small */ + // /* stay with this stepsize unless err really small */ // if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) { newtime = forcetime; - //} + // } } else { if (err > SoftHeunTol / 2.0f) { /* stay with this stepsize unless err really small */ diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index fcb992e1535..c61fa793367 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -137,24 +137,23 @@ static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_addres { bSound *sound = (bSound *)id; const bool is_undo = BLO_write_is_undo(writer); - if (sound->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - sound->tags = 0; - sound->handle = NULL; - sound->playback_handle = NULL; - sound->spinlock = NULL; - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) { - sound->packedfile = NULL; - } - - /* write LibData */ - BLO_write_id_struct(writer, bSound, id_address, &sound->id); - BKE_id_blend_write(writer, &sound->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + sound->tags = 0; + sound->handle = NULL; + sound->playback_handle = NULL; + sound->spinlock = NULL; - BKE_packedfile_blend_write(writer, sound->packedfile); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) { + sound->packedfile = NULL; } + + /* write LibData */ + BLO_write_id_struct(writer, bSound, id_address, &sound->id); + BKE_id_blend_write(writer, &sound->id); + + BKE_packedfile_blend_write(writer, sound->packedfile); } static void sound_blend_read_data(BlendDataReader *reader, ID *id) @@ -703,13 +702,13 @@ void *BKE_sound_scene_add_scene_sound( Scene *scene, Sequence *sequence, int startframe, int endframe, int frameskip) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene && scene != sequence->scene) { + if (sequence->scene && scene != sequence->scene && sequence->sound) { const double fps = FPS; return AUD_Sequence_add(scene->sound_scene, sequence->scene->sound_scene, startframe / fps, endframe / fps, - frameskip / fps); + frameskip / fps + sequence->sound->offset_time); } return NULL; } @@ -737,7 +736,7 @@ void *BKE_sound_add_scene_sound( sequence->sound->playback_handle, startframe / fps, endframe / fps, - frameskip / fps); + frameskip / fps + sequence->sound->offset_time); AUD_SequenceEntry_setMuted(handle, (sequence->flag & SEQ_MUTE) != 0); AUD_SequenceEntry_setAnimationData(handle, AUD_AP_VOLUME, CFRA, &sequence->volume, 0); AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, CFRA, &sequence->pitch, 0); @@ -765,22 +764,23 @@ void BKE_sound_mute_scene_sound(void *handle, char mute) } void BKE_sound_move_scene_sound( - Scene *scene, void *handle, int startframe, int endframe, int frameskip) + Scene *scene, void *handle, int startframe, int endframe, int frameskip, double audio_offset) { sound_verify_evaluated_id(&scene->id); const double fps = FPS; - AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps); + AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps + audio_offset); } void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene_sound) { + if (sequence->scene_sound && sequence->sound) { BKE_sound_move_scene_sound(scene, sequence->scene_sound, sequence->startdisp, sequence->enddisp, - sequence->startofs + sequence->anim_startofs); + sequence->startofs + sequence->anim_startofs, + sequence->sound->offset_time); } } @@ -1213,6 +1213,7 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; + sound_info->start_offset = info.start_offset; return true; } @@ -1310,7 +1311,8 @@ void BKE_sound_move_scene_sound(Scene *UNUSED(scene), void *UNUSED(handle), int UNUSED(startframe), int UNUSED(endframe), - int UNUSED(frameskip)) + int UNUSED(frameskip), + double UNUSED(audio_offset)) { } void BKE_sound_move_scene_sound_defaults(Scene *UNUSED(scene), Sequence *UNUSED(sequence)) diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index af9b2268879..4b10522c375 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -56,14 +56,13 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data) static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Speaker *spk = (Speaker *)id; - if (spk->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Speaker, id_address, &spk->id); - BKE_id_blend_write(writer, &spk->id); - - if (spk->adt) { - BKE_animdata_blend_write(writer, spk->adt); - } + + /* write LibData */ + BLO_write_id_struct(writer, Speaker, id_address, &spk->id); + BKE_id_blend_write(writer, &spk->id); + + if (spk->adt) { + BKE_animdata_blend_write(writer, spk->adt); } } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index a7caae967f6..dda7abea0fc 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -189,7 +189,11 @@ static float3 direction_bisect(const float3 &prev, const float3 &middle, const f const float3 dir_prev = (middle - prev).normalized(); const float3 dir_next = (next - middle).normalized(); - return (dir_prev + dir_next).normalized(); + const float3 result = (dir_prev + dir_next).normalized(); + if (UNLIKELY(result.is_zero())) { + return float3(0.0f, 0.0f, 1.0f); + } + return result; } static void calculate_tangents(Span<float3> positions, @@ -197,6 +201,7 @@ static void calculate_tangents(Span<float3> positions, MutableSpan<float3> tangents) { if (positions.size() == 1) { + tangents.first() = float3(0.0f, 0.0f, 1.0f); return; } @@ -237,13 +242,8 @@ Span<float3> Spline::evaluated_tangents() const Span<float3> positions = this->evaluated_positions(); - if (eval_size == 1) { - evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f); - } - else { - calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); - this->correct_end_tangents(); - } + calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_); + this->correct_end_tangents(); tangent_cache_dirty_ = false; return evaluated_tangents_cache_; diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index dc5162f201e..95436372a65 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -475,7 +475,7 @@ static void studiolight_load_equirect_image(StudioLight *sl) NULL, (failed || (specular_ibuf == NULL)) ? magenta : black, 1, 1, 4); } - if ((sl->flag & STUDIOLIGHT_TYPE_MATCAP)) { + if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) { sl->matcap_diffuse.ibuf = diffuse_ibuf; sl->matcap_specular.ibuf = specular_ibuf; if (specular_ibuf != NULL) { @@ -1192,7 +1192,7 @@ static void studiolight_add_files_from_datafolder(const int folder_id, uint totfile = BLI_filelist_dir_contents(folder, &dir); int i; for (i = 0; i < totfile; i++) { - if ((dir[i].type & S_IFREG)) { + if (dir[i].type & S_IFREG) { studiolight_add_file(dir[i].path, flag); } } @@ -1473,7 +1473,7 @@ struct StudioLight *BKE_studiolight_find_default(int flag) } LISTBASE_FOREACH (StudioLight *, sl, &studiolights) { - if ((sl->flag & flag)) { + if (sl->flag & flag) { return sl; } } @@ -1484,7 +1484,7 @@ struct StudioLight *BKE_studiolight_find(const char *name, int flag) { LISTBASE_FOREACH (StudioLight *, sl, &studiolights) { if (STREQLEN(sl->name, name, FILE_MAXFILE)) { - if ((sl->flag & flag)) { + if (sl->flag & flag) { return sl; } @@ -1542,32 +1542,32 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) return; } - if ((flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED)) { + if (flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED) { studiolight_load_equirect_image(sl); } - if ((flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED)) { + if (flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED) { studiolight_calculate_radiance_cubemap_buffers(sl); } - if ((flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED)) { + if (flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED) { if (!studiolight_load_spherical_harmonics_coefficients(sl)) { studiolight_calculate_diffuse_light(sl); } } - if ((flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE) { studiolight_create_equirect_radiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE) { studiolight_create_equirect_irradiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED)) { + if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED) { if (!studiolight_load_irradiance_equirect_image(sl)) { studiolight_calculate_irradiance_equirect_image(sl); } } - if ((flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE) { studiolight_create_matcap_diffuse_gputexture(sl); } - if ((flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE)) { + if (flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE) { studiolight_create_matcap_specular_gputexture(sl); } } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 80ff8ce9162..275cf0d4c38 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -171,9 +171,6 @@ static void text_free_data(ID *id) static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address) { - if (id->us < 1 && !BLO_write_is_undo(writer)) { - return; - } Text *text = (Text *)id; /* NOTE: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */ @@ -2331,7 +2328,7 @@ int txt_setcurr_tab_spaces(Text *text, int space) } while (text->curl->line[i] == indent) { - // we only count those tabs/spaces that are before any text or before the curs; + /* We only count those tabs/spaces that are before any text or before the curs; */ if (i == text->curc) { return i; } diff --git a/source/blender/blenkernel/intern/text_suggestions.c b/source/blender/blenkernel/intern/text_suggestions.c index d717b88e8ca..e93e969cb33 100644 --- a/source/blender/blenkernel/intern/text_suggestions.c +++ b/source/blender/blenkernel/intern/text_suggestions.c @@ -57,10 +57,7 @@ static void txttl_free_suggest(void) static void txttl_free_docs(void) { - if (documentation) { - MEM_freeN(documentation); - documentation = NULL; - } + MEM_SAFE_FREE(documentation); } /**************************/ @@ -240,10 +237,7 @@ void texttool_docs_show(const char *docs) len = strlen(docs); - if (documentation) { - MEM_freeN(documentation); - documentation = NULL; - } + MEM_SAFE_FREE(documentation); /* Ensure documentation ends with a '\n' */ if (docs[len - 1] != '\n') { diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 29c41b88135..228e6fffdf7 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -150,28 +150,27 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Tex *tex = (Tex *)id; - if (tex->id.us > 0 || BLO_write_is_undo(writer)) { - /* write LibData */ - BLO_write_id_struct(writer, Tex, id_address, &tex->id); - BKE_id_blend_write(writer, &tex->id); - if (tex->adt) { - BKE_animdata_blend_write(writer, tex->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Tex, id_address, &tex->id); + BKE_id_blend_write(writer, &tex->id); - /* direct data */ - if (tex->coba) { - BLO_write_struct(writer, ColorBand, tex->coba); - } + if (tex->adt) { + BKE_animdata_blend_write(writer, tex->adt); + } - /* nodetree is integral part of texture, no libdata */ - if (tex->nodetree) { - BLO_write_struct(writer, bNodeTree, tex->nodetree); - ntreeBlendWrite(writer, tex->nodetree); - } + /* direct data */ + if (tex->coba) { + BLO_write_struct(writer, ColorBand, tex->coba); + } - BKE_previewimg_blend_write(writer, tex->preview); + /* nodetree is integral part of texture, no libdata */ + if (tex->nodetree) { + BLO_write_struct(writer, bNodeTree, tex->nodetree); + ntreeBlendWrite(writer, tex->nodetree); } + + BKE_previewimg_blend_write(writer, tex->preview); } static void texture_blend_read_data(BlendDataReader *reader, ID *id) @@ -490,9 +489,8 @@ void set_current_linestyle_texture(FreestyleLineStyle *linestyle, Tex *newtex) linestyle->mtex[act]->tex = newtex; id_us_plus(&newtex->id); } - else if (linestyle->mtex[act]) { - MEM_freeN(linestyle->mtex[act]); - linestyle->mtex[act] = NULL; + else { + MEM_SAFE_FREE(linestyle->mtex[act]); } } @@ -595,9 +593,8 @@ void set_current_particle_texture(ParticleSettings *part, Tex *newtex) part->mtex[act]->tex = newtex; id_us_plus(&newtex->id); } - else if (part->mtex[act]) { - MEM_freeN(part->mtex[act]); - part->mtex[act] = NULL; + else { + MEM_SAFE_FREE(part->mtex[act]); } } @@ -660,14 +657,8 @@ void BKE_texture_pointdensity_free_data(PointDensity *pd) BLI_bvhtree_free(pd->point_tree); pd->point_tree = NULL; } - if (pd->point_data) { - MEM_freeN(pd->point_data); - pd->point_data = NULL; - } - if (pd->coba) { - MEM_freeN(pd->coba); - pd->coba = NULL; - } + MEM_SAFE_FREE(pd->point_data); + MEM_SAFE_FREE(pd->coba); BKE_curvemapping_free(pd->falloff_curve); /* can be NULL */ } diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index f107fc4d6bc..92ff0911cf3 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -322,7 +322,7 @@ static bool tracking_check_marker_margin(const libmv_Marker *libmv_marker, static bool autotrack_is_marker_usable(const MovieTrackingMarker *marker) { - if ((marker->flag & MARKER_DISABLED)) { + if (marker->flag & MARKER_DISABLED) { return false; } return true; @@ -797,7 +797,7 @@ void BKE_autotrack_context_finish(AutoTrackContext *context) clip, context->start_scene_frame); LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { - if ((plane_track->flag & PLANE_TRACK_AUTOKEY)) { + if (plane_track->flag & PLANE_TRACK_AUTOKEY) { continue; } for (int track_index = 0; track_index < context->num_all_tracks; track_index++) { diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index b28d17df814..69452d6896f 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -578,27 +578,26 @@ static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_addre { Volume *volume = (Volume *)id; const bool is_undo = BLO_write_is_undo(writer); - if (volume->id.us > 0 || is_undo) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - volume->runtime.grids = nullptr; - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) { - volume->packedfile = nullptr; - } + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + volume->runtime.grids = nullptr; - /* write LibData */ - BLO_write_id_struct(writer, Volume, id_address, &volume->id); - BKE_id_blend_write(writer, &volume->id); + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) { + volume->packedfile = nullptr; + } - /* direct data */ - BLO_write_pointer_array(writer, volume->totcol, volume->mat); - if (volume->adt) { - BKE_animdata_blend_write(writer, volume->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, Volume, id_address, &volume->id); + BKE_id_blend_write(writer, &volume->id); - BKE_packedfile_blend_write(writer, volume->packedfile); + /* direct data */ + BLO_write_pointer_array(writer, volume->totcol, volume->mat); + if (volume->adt) { + BKE_animdata_blend_write(writer, volume->adt); } + + BKE_packedfile_blend_write(writer, volume->packedfile); } static void volume_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index 7ab67516242..e9d6eea4614 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -159,7 +159,7 @@ static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts, } BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); return mesh; } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 059dc68b1dc..329633c6759 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -58,7 +58,7 @@ static void workspace_init_data(ID *id) { WorkSpace *workspace = (WorkSpace *)id; - BKE_asset_library_reference_init_default(&workspace->asset_library); + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); } static void workspace_free_data(ID *id) diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index e889d8af1d5..4abe1ff0f20 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -138,26 +138,25 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data) static void world_blend_write(BlendWriter *writer, ID *id, const void *id_address) { World *wrld = (World *)id; - if (wrld->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - BLI_listbase_clear(&wrld->gpumaterial); - /* write LibData */ - BLO_write_id_struct(writer, World, id_address, &wrld->id); - BKE_id_blend_write(writer, &wrld->id); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&wrld->gpumaterial); - if (wrld->adt) { - BKE_animdata_blend_write(writer, wrld->adt); - } + /* write LibData */ + BLO_write_id_struct(writer, World, id_address, &wrld->id); + BKE_id_blend_write(writer, &wrld->id); - /* nodetree is integral part of world, no libdata */ - if (wrld->nodetree) { - BLO_write_struct(writer, bNodeTree, wrld->nodetree); - ntreeBlendWrite(writer, wrld->nodetree); - } + if (wrld->adt) { + BKE_animdata_blend_write(writer, wrld->adt); + } - BKE_previewimg_blend_write(writer, wrld->preview); + /* nodetree is integral part of world, no libdata */ + if (wrld->nodetree) { + BLO_write_struct(writer, bNodeTree, wrld->nodetree); + ntreeBlendWrite(writer, wrld->nodetree); } + + BKE_previewimg_blend_write(writer, wrld->preview); } static void world_blend_read_data(BlendDataReader *reader, ID *id) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9b3103a638b..323da7473b5 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -149,7 +149,6 @@ static int write_audio_frame(FFMpegContext *context) AUD_Device_read( context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); - context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate; frame = av_frame_alloc(); frame->pts = context->audio_time / av_q2d(c->time_base); @@ -184,7 +183,7 @@ static int write_audio_frame(FFMpegContext *context) context->audio_input_samples * c->channels * context->audio_sample_size, 1); - int success = 0; + int success = 1; int ret = avcodec_send_frame(c, frame); if (ret < 0) { @@ -369,7 +368,7 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R return success; } -/* read and encode a frame of audio from the buffer */ +/* read and encode a frame of video from the buffer */ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels) { AVCodecParameters *codec = context->video_stream->codecpar; @@ -741,7 +740,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - if ((of->oformat->flags & AVFMT_GLOBALHEADER)) { + if (of->oformat->flags & AVFMT_GLOBALHEADER) { PRINT("Using global header\n"); c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } @@ -1226,9 +1225,8 @@ fail: * parameter. * </p> */ -static void flush_ffmpeg(FFMpegContext *context) +static void flush_ffmpeg(AVCodecContext *c, AVStream *stream, AVFormatContext *outfile) { - AVCodecContext *c = context->video_codec; AVPacket *packet = av_packet_alloc(); avcodec_send_frame(c, NULL); @@ -1247,13 +1245,13 @@ static void flush_ffmpeg(FFMpegContext *context) break; } - packet->stream_index = context->video_stream->index; - av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); + packet->stream_index = stream->index; + av_packet_rescale_ts(packet, c->time_base, stream->time_base); # ifdef FFMPEG_USE_DURATION_WORKAROUND - my_guess_pkt_duration(context->outfile, context->video_stream, packet); + my_guess_pkt_duration(context->outfile, stream, packet); # endif - int write_ret = av_interleaved_write_frame(context->outfile, packet); + int write_ret = av_interleaved_write_frame(outfile, packet); if (write_ret != 0) { fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret)); break; @@ -1396,12 +1394,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit); # ifdef WITH_AUDASPACE static void write_audio_frames(FFMpegContext *context, double to_pts) { - int finished = 0; + AVCodecContext *c = context->audio_codec; - while (context->audio_stream && !finished) { - if ((context->audio_time >= to_pts) || (write_audio_frame(context))) { - finished = 1; + while (context->audio_stream) { + if ((context->audio_time >= to_pts) || !write_audio_frame(context)) { + break; } + context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate; } } # endif @@ -1422,9 +1421,6 @@ int BKE_ffmpeg_append(void *context_v, PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, rectx, recty); - /* why is this done before writing the video frame and again at end_ffmpeg? */ - // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); - if (context->video_stream) { avframe = generate_video_frame(context, (unsigned char *)pixels); success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports)); @@ -1439,8 +1435,9 @@ int BKE_ffmpeg_append(void *context_v, } # ifdef WITH_AUDASPACE - write_audio_frames(context, - (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); + /* Add +1 frame because we want to encode audio up until the next video frame. */ + write_audio_frames( + context, (frame - start_frame + 1) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); # endif return success; } @@ -1461,8 +1458,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) # endif if (context->video_stream) { - PRINT("Flushing delayed frames...\n"); - flush_ffmpeg(context); + PRINT("Flushing delayed video frames...\n"); + flush_ffmpeg(context->video_codec, context->video_stream, context->outfile); + } + + if (context->audio_stream) { + PRINT("Flushing delayed audio frames...\n"); + flush_ffmpeg(context->audio_codec, context->audio_stream, context->outfile); } if (context->outfile) { diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index 6bf29a6168f..084f573e8c7 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -156,7 +156,7 @@ void _bli_array_grow_func(void **arr_p, * \{ */ /** - * not part of the 'API' but handy funcs, + * Not part of the 'API' but handy functions, * same purpose as #BLI_array_staticdeclare() * but use when the max size is known ahead of time */ #define BLI_array_fixedstack_declare(arr, maxstatic, realsize, allocstr) \ diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index 3051d980d45..b5981028893 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 7cfecc798a7..906a56ce909 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -154,18 +154,17 @@ bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL bool BLI_file_touch(const char *file) ATTR_NONNULL(); bool BLI_file_alias_target(const char *filepath, char *r_targetpath) ATTR_WARN_UNUSED_RESULT; -#if 0 /* UNUSED */ -int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -#endif -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(); -size_t BLI_gzip_mem_to_file_at_pos(void *buf, - size_t len, - FILE *file, - size_t gz_stream_offset, - int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) +bool BLI_file_magic_is_gzip(const char header[4]); + +size_t BLI_file_zstd_from_mem_at_pos(void *buf, + size_t len, + FILE *file, + size_t file_offset, + int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BLI_file_magic_is_zstd(const char header[4]); + size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT; size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h new file mode 100644 index 00000000000..8d1fa3d1596 --- /dev/null +++ b/source/blender/blenlib/BLI_filereader.h @@ -0,0 +1,81 @@ +/* + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bli + * \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...). + */ + +#pragma once + +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include <sys/types.h> +#endif + +#include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" + +#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) +typedef int64_t off64_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct FileReader; + +typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size); +typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence); +typedef void (*FileReaderCloseFn)(struct FileReader *reader); + +/* General structure for all FileReaders, implementations add custom fields at the end. */ +typedef struct FileReader { + FileReaderReadFn read; + FileReaderSeekFn seek; + FileReaderCloseFn close; + + off64_t offset; +} FileReader; + +/* Functions for opening the various types of FileReader. + * They either succeed and return a valid FileReader, or fail and return NULL. + * + * If a FileReader is created, it has to be cleaned up and freed by calling + * its close() function unless another FileReader has taken ownership - for example, + * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. + */ + +/* Create FileReader from raw file descriptor. */ +FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from raw file descriptor using memory-mapped IO. */ +FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; +/* Create FileReader from a region of memory. */ +FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +/* Create FileReader from applying Zstd decompression on an underlying file. */ +FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/* Create FileReader from applying Gzip decompression on an underlying file. */ +FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index f04c0e9c80a..7a3169520ca 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -58,11 +58,7 @@ class IndexMask { */ IndexMask(Span<int64_t> indices) : indices_(indices) { -#ifdef DEBUG - for (int64_t i = 1; i < indices.size(); i++) { - BLI_assert(indices[i - 1] < indices[i]); - } -#endif + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); } /** @@ -94,6 +90,22 @@ class IndexMask { { } + /** Checks that the indices are non-negative and in ascending order. */ + static bool indices_are_valid_index_mask(Span<int64_t> indices) + { + if (!indices.is_empty()) { + if (indices.first() < 0) { + return false; + } + } + for (int64_t i = 1; i < indices.size(); i++) { + if (indices[i - 1] >= indices[i]) { + return false; + } + } + return true; + } + operator Span<int64_t>() const { return indices_; @@ -204,6 +216,11 @@ class IndexMask { { return indices_.size(); } + + bool is_empty() const + { + return indices_.is_empty(); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 88dc20a64f2..e877503e835 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -120,6 +120,9 @@ MINLINE double interpd(double a, double b, double t); MINLINE float ratiof(float min, float max, float pos); MINLINE double ratiod(double min, double max, double pos); +MINLINE float scalenorm(float a, float b, float x); +MINLINE double scalenormd(double a, double b, double x); + /* NOTE: Compilers will upcast all types smaller than int to int when performing arithmetic * operation. */ MINLINE int square_s(short a); diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 9ac14a6edfe..bcda25ca533 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -682,14 +682,14 @@ void planes_from_projmat(const float mat[4][4], float near[4], float far[4]); -void projmat_dimensions(const float projmat[4][4], +void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, float *r_top, float *r_near, float *r_far); -void projmat_dimensions_db(const float projmat[4][4], +void projmat_dimensions_db(const float winmat[4][4], double *r_left, double *r_right, double *r_bottom, diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 2f4cf1721af..860ba14a3ed 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -185,6 +185,7 @@ MINLINE void negate_v3_db(double r[3]); MINLINE void invert_v2(float r[2]); MINLINE void invert_v3(float r[3]); +MINLINE void invert_v3_safe(float r[3]); /* Invert the vector, but leaves zero values as zero. */ MINLINE void abs_v2(float r[2]); MINLINE void abs_v2_v2(float r[2], const float a[2]); diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/blenlib/BLI_range.h index 96664fa368e..e55f443769d 100644 --- a/source/blender/functions/FN_multi_function_network_optimization.hh +++ b/source/blender/blenlib/BLI_range.h @@ -16,14 +16,24 @@ #pragma once -#include "FN_multi_function_network.hh" +/** \file + * \ingroup bli + */ -#include "BLI_resource_scope.hh" +#ifdef __cplusplus +extern "C" { +#endif -namespace blender::fn::mf_network_optimization { +typedef struct Range2f { + float min; + float max; +} Range2f; -void dead_node_removal(MFNetwork &network); -void constant_folding(MFNetwork &network, ResourceScope &scope); -void common_subnetwork_elimination(MFNetwork &network); +BLI_INLINE bool range2f_in_range(const Range2f *range, const float value) +{ + return IN_RANGE(value, range->min, range->max); +} -} // namespace blender::fn::mf_network_optimization +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index 0953e3f1946..bf09b56c779 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -45,7 +45,7 @@ #undef small -// These definitions are also in BLI_math for simplicity +/* These definitions are also in BLI_math for simplicity. */ #ifdef __cplusplus extern "C" { @@ -72,7 +72,7 @@ extern "C" { #if defined(_MSC_VER) # define R_OK 4 # define W_OK 2 -// not accepted by access() on windows +/* Not accepted by `access()` on windows. */ //# define X_OK 1 # define F_OK 0 #endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ea5572f1c8a..f98d15ad08b 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS} ) @@ -75,6 +76,10 @@ set(SRC intern/endian_switch.c intern/expr_pylike_eval.c intern/fileops.c + intern/filereader_file.c + intern/filereader_gzip.c + intern/filereader_memory.c + intern/filereader_zstd.c intern/fnmatch.c intern/freetypefont.c intern/gsqueue.c @@ -194,6 +199,7 @@ set(SRC BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h + BLI_filereader.h BLI_fileops_types.h BLI_float2.hh BLI_float3.hh @@ -265,6 +271,7 @@ set(SRC BLI_quadric.h BLI_rand.h BLI_rand.hh + BLI_range.h BLI_rect.h BLI_resource_scope.hh BLI_scanfill.h @@ -322,6 +329,7 @@ set(LIB ${FREETYPE_LIBRARY} ${ZLIB_LIBRARIES} + ${ZSTD_LIBRARIES} ) if(WITH_MEM_VALGRIND) diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index ea6c2d8d498..4a07f1134d0 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -409,8 +409,8 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r for (i = 0; i < verts_pack_len && isect; i++) { vert = &vs_ctx.vertarray[vertex_pack_indices[i]]; - /* printf("\ttesting vert %i %i %i %f %f\n", i, - * vert->free, verts_pack_len, vert->x, vert->y); */ + // printf("\ttesting vert %i %i %i %f %f\n", i, + // vert->free, verts_pack_len, vert->x, vert->y); /* This vert has a free quadrant * Test if we can place the box here diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index ac034d2b5cd..532a29b8147 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -31,6 +31,7 @@ #include <errno.h> #include "zlib.h" +#include "zstd.h" #ifdef WIN32 # include "BLI_fileops_types.h" @@ -61,200 +62,124 @@ #include "BLI_sys_types.h" /* for intptr_t support */ #include "BLI_utildefines.h" -#if 0 /* UNUSED */ -/* gzip the file in from and write it to "to". - * return -1 if zlib fails, -2 if the originating file does not exist - * NOTE: will remove the "from" file - */ -int BLI_file_gzip(const char *from, const char *to) +size_t BLI_file_zstd_from_mem_at_pos( + void *buf, size_t len, FILE *file, size_t file_offset, int compression_level) { - char buffer[10240]; - int file; - int readsize = 0; - int rval = 0, err; - gzFile gzfile; + fseek(file, file_offset, SEEK_SET); - /* level 1 is very close to 3 (the default) in terms of file size, - * but about twice as fast, best use for speedy saving - campbell */ - gzfile = BLI_gzopen(to, "wb1"); - if (gzfile == NULL) { - return -1; - } - file = BLI_open(from, O_BINARY | O_RDONLY, 0); - if (file == -1) { - return -2; - } + ZSTD_CCtx *ctx = ZSTD_createCCtx(); + ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level); + + ZSTD_inBuffer input = {buf, len, 0}; - while (1) { - readsize = read(file, buffer, sizeof(buffer)); + size_t out_len = ZSTD_CStreamOutSize(); + void *out_buf = MEM_mallocN(out_len, __func__); + size_t total_written = 0; - if (readsize < 0) { - rval = -2; /* error happened in reading */ - fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno)); + /* Compress block and write it out until the input has been consumed. */ + while (input.pos < input.size) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue); + if (ZSTD_isError(ret)) { break; } - else if (readsize == 0) { - break; /* done reading */ + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { + break; } + total_written += output.pos; + } - if (gzwrite(gzfile, buffer, readsize) <= 0) { - rval = -1; /* error happened in writing */ - fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err)); + /* Finalize the Zstd frame. */ + size_t ret = 1; + while (ret != 0) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end); + if (ZSTD_isError(ret)) { + break; + } + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - close(file); + MEM_freeN(out_buf); + ZSTD_freeCCtx(ctx); - return rval; + return ZSTD_isError(ret) ? 0 : total_written; } -#endif -/* gzip the file in from_file and write it to memory to_mem, at most size bytes. - * return the unzipped size - */ -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) { - gzFile gzfile; - int readsize, size, alloc_size = 0; - char *mem = NULL; - const int chunk_size = 512 * 1024; + fseek(file, file_offset, SEEK_SET); - size = 0; + ZSTD_DCtx *ctx = ZSTD_createDCtx(); - gzfile = BLI_gzopen(from_file, "rb"); - for (;;) { - if (mem == NULL) { - mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem"); - alloc_size = chunk_size; - } - else { - mem = MEM_reallocN(mem, size + chunk_size); - alloc_size += chunk_size; - } + size_t in_len = ZSTD_DStreamInSize(); + void *in_buf = MEM_mallocN(in_len, __func__); + ZSTD_inBuffer input = {in_buf, in_len, 0}; - readsize = gzread(gzfile, mem + size, chunk_size); - if (readsize > 0) { - size += readsize; - } - else { + ZSTD_outBuffer output = {buf, len, 0}; + + size_t ret = 0; + /* Read and decompress chunks of input data until we have enough output. */ + while (output.pos < output.size && !ZSTD_isError(ret)) { + input.size = fread(in_buf, 1, in_len, file); + if (input.size == 0) { break; } - } - gzclose(gzfile); + /* Consume input data until we run out or have enough output. */ + input.pos = 0; + while (input.pos < input.size && output.pos < output.size) { + ret = ZSTD_decompressStream(ctx, &output, &input); - if (size == 0) { - MEM_freeN(mem); - mem = NULL; - } - else if (alloc_size != size) { - mem = MEM_reallocN(mem, size); + if (ZSTD_isError(ret)) { + break; + } + } } - *r_size = size; + MEM_freeN(in_buf); + ZSTD_freeDCtx(ctx); - return mem; + return ZSTD_isError(ret) ? 0 : output.pos; } -#define CHUNK (256 * 1024) - -/* gzip byte array from memory and write it to file at certain position. - * return size of gzip stream. - */ -size_t BLI_gzip_mem_to_file_at_pos( - void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level) +bool BLI_file_magic_is_gzip(const char header[4]) { - int ret, flush; - unsigned have; - z_stream strm; - unsigned char out[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, compression_level); - if (ret != Z_OK) { - return 0; - } - - strm.avail_in = len; - strm.next_in = (Bytef *)buf; - flush = Z_FINISH; - - do { - strm.avail_out = CHUNK; - strm.next_out = out; - ret = deflate(&strm, flush); - if (ret == Z_STREAM_ERROR) { - return 0; - } - have = CHUNK - strm.avail_out; - if (fwrite(out, 1, have, file) != have || ferror(file)) { - deflateEnd(&strm); - return 0; - } - } while (strm.avail_out == 0); - - if (strm.avail_in != 0 || ret != Z_STREAM_END) { - return 0; - } - - deflateEnd(&strm); - return (size_t)strm.total_out; + /* GZIP itself starts with the magic bytes 0x1f 0x8b. + * The third byte indicates the compression method, which is 0x08 for DEFLATE. */ + return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; } -/* read and decompress gzip stream from file at certain position to buffer. - * return size of decompressed data. - */ -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) +bool BLI_file_magic_is_zstd(const char header[4]) { - int ret; - z_stream strm; - size_t chunk = 256 * 1024; - unsigned char in[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); - if (ret != Z_OK) { - return 0; + /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame. + * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5* + * for skippable frames, with the * being anything from 0 to F. + * + * To check whether a file is Zstd-compressed, we just check whether the first frame matches + * either. Seeking through the file until a Zstd frame is found would make things more + * complicated and the probability of a false positive is rather low anyways. + * + * Note that LZ4 uses a compatible format, so even though its compressed frames have a + * different magic number, a valid LZ4 file might also start with a skippable frame matching + * the second check here. + * + * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md + */ + + uint32_t magic = *((uint32_t *)header); + if (magic == 0xFD2FB528) { + return true; } - - do { - strm.avail_in = fread(in, 1, chunk, file); - strm.next_in = in; - if (ferror(file)) { - inflateEnd(&strm); - return 0; - } - - do { - strm.avail_out = len; - strm.next_out = (Bytef *)buf + strm.total_out; - - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) { - return 0; - } - } while (strm.avail_out == 0); - - } while (ret != Z_STREAM_END); - - inflateEnd(&strm); - return (size_t)strm.total_out; + if ((magic >> 4) == 0x184D2A5) { + return true; + } + return false; } -#undef CHUNK - /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c new file mode 100644 index 00000000000..3a833871e27 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_file.c @@ -0,0 +1,80 @@ +/* + * 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) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#ifndef WIN32 +# include <unistd.h> /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include <io.h> /* for open close read */ +#endif + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + int filedes; +} RawFileReader; + +static ssize_t file_read(FileReader *reader, void *buffer, size_t size) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + ssize_t readsize = read(rawfile->filedes, buffer, size); + + if (readsize >= 0) { + rawfile->reader.offset += readsize; + } + + return readsize; +} + +static off64_t file_seek(FileReader *reader, off64_t offset, int whence) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence); + return rawfile->reader.offset; +} + +static void file_close(FileReader *reader) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + close(rawfile->filedes); + MEM_freeN(rawfile); +} + +FileReader *BLI_filereader_new_file(int filedes) +{ + RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__); + + rawfile->filedes = filedes; + + rawfile->reader.read = file_read; + rawfile->reader.seek = file_seek; + rawfile->reader.close = file_close; + + return (FileReader *)rawfile; +} diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c new file mode 100644 index 00000000000..72eb153a8b9 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -0,0 +1,108 @@ +/* + * 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) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <zlib.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + z_stream strm; + + void *in_buf; + size_t in_size; +} GzipReader; + +static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) +{ + GzipReader *gzip = (GzipReader *)reader; + + gzip->strm.avail_out = size; + gzip->strm.next_out = buffer; + + while (gzip->strm.avail_out > 0) { + if (gzip->strm.avail_in == 0) { + /* Ran out of buffered input data, read some more. */ + size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + gzip->strm.avail_in = readsize; + gzip->strm.next_in = gzip->in_buf; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + int ret = inflate(&gzip->strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + break; + } + } + + ssize_t read_len = size - gzip->strm.avail_out; + gzip->reader.offset += read_len; + return read_len; +} + +static void gzip_close(FileReader *reader) +{ + GzipReader *gzip = (GzipReader *)reader; + + if (inflateEnd(&gzip->strm) != Z_OK) { + printf("close gzip stream error\n"); + } + MEM_freeN((void *)gzip->in_buf); + + gzip->base->close(gzip->base); + MEM_freeN(gzip); +} + +FileReader *BLI_filereader_new_gzip(FileReader *base) +{ + GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__); + gzip->base = base; + + if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) { + MEM_freeN(gzip); + return NULL; + } + + gzip->in_size = 256 * 2014; + gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf"); + + gzip->reader.read = gzip_read; + gzip->reader.seek = NULL; + gzip->reader.close = gzip_close; + + return (FileReader *)gzip; +} diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c new file mode 100644 index 00000000000..150fed7d1cc --- /dev/null +++ b/source/blender/blenlib/intern/filereader_memory.c @@ -0,0 +1,145 @@ +/* + * 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) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <string.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" +#include "BLI_mmap.h" + +#include "MEM_guardedalloc.h" + +/* This file implements both memory-backed and memory-mapped-file-backed reading. */ +typedef struct { + FileReader reader; + + const char *data; + BLI_mmap_file *mmap; + size_t length; +} MemoryReader; + +static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + memcpy(buffer, mem->data + mem->reader.offset, readsize); + mem->reader.offset += readsize; + + return readsize; +} + +static off64_t memory_seek(FileReader *reader, off64_t offset, int whence) +{ + MemoryReader *mem = (MemoryReader *)reader; + + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = mem->reader.offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = mem->length + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > mem->length) { + return -1; + } + + mem->reader.offset = new_pos; + return mem->reader.offset; +} + +static void memory_close_raw(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLI_filereader_new_memory(const void *data, size_t len) +{ + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->data = (const char *)data; + mem->length = len; + + mem->reader.read = memory_read_raw; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_raw; + + return (FileReader *)mem; +} + +/* Memory-mapped file reading. + * By using `mmap()`, we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with `memcpy()` etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) { + return 0; + } + + mem->reader.offset += readsize; + + return readsize; +} + +static void memory_close_mmap(FileReader *reader) +{ + MemoryReader *mem = (MemoryReader *)reader; + BLI_mmap_free(mem->mmap); + MEM_freeN(mem); +} + +FileReader *BLI_filereader_new_mmap(int filedes) +{ + BLI_mmap_file *mmap = BLI_mmap_open(filedes); + if (mmap == NULL) { + return NULL; + } + + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->mmap = mmap; + mem->length = BLI_lseek(filedes, 0, SEEK_END); + + mem->reader.read = memory_read_mmap; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_mmap; + + return (FileReader *)mem; +} diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c new file mode 100644 index 00000000000..785a40cd1a1 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -0,0 +1,335 @@ +/* + * 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 bli + */ + +#include <string.h> +#include <zstd.h> + +#include "BLI_blenlib.h" +#include "BLI_endian_switch.h" +#include "BLI_filereader.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + ZSTD_DCtx *ctx; + ZSTD_inBuffer in_buf; + size_t in_buf_max_size; + + struct { + int num_frames; + size_t *compressed_ofs; + size_t *uncompressed_ofs; + + char *cached_content; + int cached_frame; + } seek; +} ZstdReader; + +static bool zstd_read_u32(FileReader *base, uint32_t *val) +{ + if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) { + return false; + } +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(val); +#endif + return true; +} + +static bool zstd_read_seek_table(ZstdReader *zstd) +{ + FileReader *base = zstd->base; + + /* The seek table frame is at the end of the file, so seek there + * and verify that there is enough data. */ + if (base->seek(base, -4, SEEK_END) < 13) { + return false; + } + uint32_t magic; + if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) { + return false; + } + + uint8_t flags; + if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) { + return false; + } + /* Bit 7 indicates checksums. Bits 5 and 6 must be zero. */ + bool has_checksums = (flags & 0x80); + if (flags & 0x60) { + return false; + } + + uint32_t num_frames; + if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) { + return false; + } + + /* Each frame has either 2 or 3 uint32_t, and after that we have + * num_frames, flags and magic for another 9 bytes. */ + uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9; + /* The frame starts with another magic number and its length, but these + * two fields are not included when counting length. */ + off64_t frame_start_ofs = 8 + expected_frame_length; + /* Sanity check: Before the start of the seek table frame, + * there must be num_frames frames, each of which at least 8 bytes long. */ + off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END); + if (seek_frame_start < num_frames * 8) { + return false; + } + + if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) { + return false; + } + + uint32_t frame_length; + if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) { + return false; + } + + zstd->seek.num_frames = num_frames; + zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + + size_t compressed_ofs = 0; + size_t uncompressed_ofs = 0; + for (int i = 0; i < num_frames; i++) { + uint32_t compressed_size, uncompressed_size; + if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) { + break; + } + if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) { + break; + } + zstd->seek.compressed_ofs[i] = compressed_ofs; + zstd->seek.uncompressed_ofs[i] = uncompressed_ofs; + compressed_ofs += compressed_size; + uncompressed_ofs += uncompressed_size; + } + zstd->seek.compressed_ofs[num_frames] = compressed_ofs; + zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs; + + /* Seek to the end of the previous frame for the following BHead frame detection. */ + if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) { + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.uncompressed_ofs); + memset(&zstd->seek, 0, sizeof(zstd->seek)); + return false; + } + + zstd->seek.cached_frame = -1; + + return true; +} + +/* Find out which frame contains the given position in the uncompressed stream. + * Basically just bisection. */ +static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos) +{ + int low = 0, high = zstd->seek.num_frames; + + if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + + while (low + 1 < high) { + int mid = low + ((high - low) >> 1); + if (zstd->seek.uncompressed_ofs[mid] <= pos) { + low = mid; + } + else { + high = mid; + } + } + + return low; +} + +/* Ensure that the currently loaded frame is the correct one. */ +static const char *zstd_ensure_cache(ZstdReader *zstd, int frame) +{ + if (zstd->seek.cached_frame == frame) { + /* Cached frame matches, so just return it. */ + return zstd->seek.cached_content; + } + + /* Cached frame doesn't match, so discard it and cache the wanted one onstead. */ + MEM_SAFE_FREE(zstd->seek.cached_content); + + size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame]; + size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] - + zstd->seek.uncompressed_ofs[frame]; + + char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__); + char *compressed_data = MEM_mallocN(compressed_size, __func__); + if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 || + zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) { + MEM_freeN(compressed_data); + MEM_freeN(uncompressed_data); + return NULL; + } + + size_t res = ZSTD_decompressDCtx( + zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size); + MEM_freeN(compressed_data); + if (ZSTD_isError(res) || res < uncompressed_size) { + MEM_freeN(uncompressed_data); + return NULL; + } + + zstd->seek.cached_frame = frame; + zstd->seek.cached_content = uncompressed_data; + return uncompressed_data; +} + +static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + size_t end_offset = zstd->reader.offset + size, read_len = 0; + while (zstd->reader.offset < end_offset) { + int frame = zstd_frame_from_pos(zstd, zstd->reader.offset); + if (frame < 0) { + /* EOF is reached, so return as much as we can. */ + break; + } + + const char *framedata = zstd_ensure_cache(zstd, frame); + if (framedata == NULL) { + /* Error while reading the frame, so return as much as we can. */ + break; + } + + size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset); + size_t frame_read_len = frame_end_offset - zstd->reader.offset; + + size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame]; + memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len); + read_len += frame_read_len; + zstd->reader.offset = frame_end_offset; + } + + return read_len; +} + +static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence) +{ + ZstdReader *zstd = (ZstdReader *)reader; + off64_t new_pos; + if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset; + } + else { + new_pos = zstd->reader.offset + offset; + } + + if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + zstd->reader.offset = new_pos; + return zstd->reader.offset; +} + +static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + ZSTD_outBuffer output = {buffer, size, 0}; + + while (output.pos < output.size) { + if (zstd->in_buf.pos == zstd->in_buf.size) { + /* Ran out of buffered input data, read some more. */ + zstd->in_buf.pos = 0; + ssize_t readsize = zstd->base->read( + zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + zstd->in_buf.size = readsize; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) { + break; + } + } + + zstd->reader.offset += output.pos; + return output.pos; +} + +static void zstd_close(FileReader *reader) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + ZSTD_freeDCtx(zstd->ctx); + if (zstd->reader.seek) { + MEM_freeN(zstd->seek.uncompressed_ofs); + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.cached_content); + } + else { + MEM_freeN((void *)zstd->in_buf.src); + } + + zstd->base->close(zstd->base); + MEM_freeN(zstd); +} + +FileReader *BLI_filereader_new_zstd(FileReader *base) +{ + ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__); + + zstd->ctx = ZSTD_createDCtx(); + zstd->base = base; + + if (zstd_read_seek_table(zstd)) { + zstd->reader.read = zstd_read_seekable; + zstd->reader.seek = zstd_seek; + } + else { + zstd->reader.read = zstd_read; + zstd->reader.seek = NULL; + + zstd->in_buf_max_size = ZSTD_DStreamInSize(); + zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf"); + zstd->in_buf.size = zstd->in_buf_max_size; + /* This signals that the buffer has run out, + * which will make the read function refill it on the first call. */ + zstd->in_buf.pos = zstd->in_buf_max_size; + } + zstd->reader.close = zstd_close; + + return (FileReader *)zstd; +} diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index 98da85e3a8c..a8b50b66f5f 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -40,6 +40,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" +#include "BLI_string_utf8.h" #include "BLI_utildefines.h" #include "BLI_vfontdata.h" @@ -286,7 +287,6 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) const FT_ULong charcode_reserve = 256; FT_ULong charcode = 0, lcode; FT_UInt glyph_index; - const char *fontname; VFontData *vfd; /* load the freetype font */ @@ -299,36 +299,29 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) /* allocate blender font */ vfd = MEM_callocN(sizeof(*vfd), "FTVFontData"); - /* get the name */ - fontname = FT_Get_Postscript_Name(face); - BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name)); + /* Get the name. */ + if (face->family_name) { + BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name); + BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name)); + } + + /* Select a character map. */ + err = FT_Select_Charmap(face, FT_ENCODING_UNICODE); + if (err) { + err = FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN); + } + if (err && face->num_charmaps > 0) { + err = FT_Select_Charmap(face, face->charmaps[0]->encoding); + } + if (err) { + FT_Done_Face(face); + MEM_freeN(vfd); + return NULL; + } /* Extract the first 256 character from TTF */ lcode = charcode = FT_Get_First_Char(face, &glyph_index); - /* No `charmap` found from the TTF so we need to figure it out. */ - if (glyph_index == 0) { - FT_CharMap found = NULL; - FT_CharMap charmap; - int n; - - for (n = 0; n < face->num_charmaps; n++) { - charmap = face->charmaps[n]; - if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) { - found = charmap; - break; - } - } - - err = FT_Set_Charmap(face, found); - - if (err) { - return NULL; - } - - lcode = charcode = FT_Get_First_Char(face, &glyph_index); - } - /* Blender default BFont is not "complete". */ const bool complete_font = (face->ascender != 0) && (face->descender != 0) && (face->ascender != face->descender); diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 6e3846e59c6..a80c495ecf3 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -199,6 +199,13 @@ MINLINE float scalenorm(float a, float b, float x) return (x * (b - a)) + a; } +/* Map a normalized value, i.e. from interval [0, 1] to interval [a, b]. */ +MINLINE double scalenormd(double a, double b, double x) +{ + BLI_assert(x <= 1 && x >= 0); + return (x * (b - a)) + a; +} + /* Used for zoom values. */ MINLINE float power_of_2(float val) { diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 803291e4a3b..8afb6b5a2be 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -1787,7 +1787,7 @@ bool isect_ray_tri_v3(const float ray_origin[3], } *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -1864,7 +1864,7 @@ bool isect_ray_tri_epsilon_v3(const float ray_origin[3], } *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -2024,7 +2024,7 @@ bool isect_ray_tri_threshold_v3(const float ray_origin[3], cross_v3_v3v3(q, s, e1); *r_lambda = f * dot_v3v3(e2, q); - if ((*r_lambda < 0.0f)) { + if (*r_lambda < 0.0f) { return false; } @@ -3325,7 +3325,7 @@ bool isect_ray_aabb_v3_simple(const float orig[3], t[5] = (double)(bb_max[2] - orig[2]) * invdirz; hit_dist[0] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); hit_dist[1] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5])); - if ((hit_dist[1] < 0.0f || hit_dist[0] > hit_dist[1])) { + if ((hit_dist[1] < 0.0f) || (hit_dist[0] > hit_dist[1])) { return false; } @@ -4962,7 +4962,7 @@ void planes_from_projmat(const float mat[4][4], } } -void projmat_dimensions(const float projmat[4][4], +void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, @@ -4970,27 +4970,27 @@ void projmat_dimensions(const float projmat[4][4], float *r_near, float *r_far) { - bool is_persp = projmat[3][3] == 0.0f; - + const bool is_persp = winmat[3][3] == 0.0f; if (is_persp) { - *r_left = (projmat[2][0] - 1.0f) / projmat[0][0]; - *r_right = (projmat[2][0] + 1.0f) / projmat[0][0]; - *r_bottom = (projmat[2][1] - 1.0f) / projmat[1][1]; - *r_top = (projmat[2][1] + 1.0f) / projmat[1][1]; - *r_near = projmat[3][2] / (projmat[2][2] - 1.0f); - *r_far = projmat[3][2] / (projmat[2][2] + 1.0f); + const float near = winmat[3][2] / (winmat[2][2] - 1.0f); + *r_left = near * ((winmat[2][0] - 1.0f) / winmat[0][0]); + *r_right = near * ((winmat[2][0] + 1.0f) / winmat[0][0]); + *r_bottom = near * ((winmat[2][1] - 1.0f) / winmat[1][1]); + *r_top = near * ((winmat[2][1] + 1.0f) / winmat[1][1]); + *r_near = near; + *r_far = winmat[3][2] / (winmat[2][2] + 1.0f); } else { - *r_left = (-projmat[3][0] - 1.0f) / projmat[0][0]; - *r_right = (-projmat[3][0] + 1.0f) / projmat[0][0]; - *r_bottom = (-projmat[3][1] - 1.0f) / projmat[1][1]; - *r_top = (-projmat[3][1] + 1.0f) / projmat[1][1]; - *r_near = (projmat[3][2] + 1.0f) / projmat[2][2]; - *r_far = (projmat[3][2] - 1.0f) / projmat[2][2]; + *r_left = (-winmat[3][0] - 1.0f) / winmat[0][0]; + *r_right = (-winmat[3][0] + 1.0f) / winmat[0][0]; + *r_bottom = (-winmat[3][1] - 1.0f) / winmat[1][1]; + *r_top = (-winmat[3][1] + 1.0f) / winmat[1][1]; + *r_near = (winmat[3][2] + 1.0f) / winmat[2][2]; + *r_far = (winmat[3][2] - 1.0f) / winmat[2][2]; } } -void projmat_dimensions_db(const float projmat_fl[4][4], +void projmat_dimensions_db(const float winmat_fl[4][4], double *r_left, double *r_right, double *r_bottom, @@ -4998,26 +4998,26 @@ void projmat_dimensions_db(const float projmat_fl[4][4], double *r_near, double *r_far) { - double projmat[4][4]; - copy_m4d_m4(projmat, projmat_fl); - - bool is_persp = projmat[3][3] == 0.0f; + double winmat[4][4]; + copy_m4d_m4(winmat, winmat_fl); + const bool is_persp = winmat[3][3] == 0.0f; if (is_persp) { - *r_left = (projmat[2][0] - 1.0) / projmat[0][0]; - *r_right = (projmat[2][0] + 1.0) / projmat[0][0]; - *r_bottom = (projmat[2][1] - 1.0) / projmat[1][1]; - *r_top = (projmat[2][1] + 1.0) / projmat[1][1]; - *r_near = projmat[3][2] / (projmat[2][2] - 1.0); - *r_far = projmat[3][2] / (projmat[2][2] + 1.0); + const double near = winmat[3][2] / (winmat[2][2] - 1.0); + *r_left = near * ((winmat[2][0] - 1.0) / winmat[0][0]); + *r_right = near * ((winmat[2][0] + 1.0) / winmat[0][0]); + *r_bottom = near * ((winmat[2][1] - 1.0) / winmat[1][1]); + *r_top = near * ((winmat[2][1] + 1.0) / winmat[1][1]); + *r_near = near; + *r_far = winmat[3][2] / (winmat[2][2] + 1.0); } else { - *r_left = (-projmat[3][0] - 1.0) / projmat[0][0]; - *r_right = (-projmat[3][0] + 1.0) / projmat[0][0]; - *r_bottom = (-projmat[3][1] - 1.0) / projmat[1][1]; - *r_top = (-projmat[3][1] + 1.0) / projmat[1][1]; - *r_near = (projmat[3][2] + 1.0) / projmat[2][2]; - *r_far = (projmat[3][2] - 1.0) / projmat[2][2]; + *r_left = (-winmat[3][0] - 1.0) / winmat[0][0]; + *r_right = (-winmat[3][0] + 1.0) / winmat[0][0]; + *r_bottom = (-winmat[3][1] - 1.0) / winmat[1][1]; + *r_top = (-winmat[3][1] + 1.0) / winmat[1][1]; + *r_near = (winmat[3][2] + 1.0) / winmat[2][2]; + *r_far = (winmat[3][2] - 1.0) / winmat[2][2]; } } diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 55f7a152b83..ddfdaffb706 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -847,6 +847,19 @@ MINLINE void invert_v3(float r[3]) r[2] = 1.0f / r[2]; } +MINLINE void invert_v3_safe(float r[3]) +{ + if (r[0] != 0.0f) { + r[0] = 1.0f / r[0]; + } + if (r[1] != 0.0f) { + r[1] = 1.0f / r[1]; + } + if (r[2] != 0.0f) { + r[2] = 1.0f / r[2]; + } +} + MINLINE void abs_v2(float r[2]) { r[0] = fabsf(r[0]); @@ -1132,6 +1145,9 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) return len_v3(d); } +/** + * \note any vectors containing `nan` will be zeroed out. + */ MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_length) { float d = dot_v2v2(a, a); @@ -1141,6 +1157,7 @@ MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float u mul_v2_v2fl(r, a, unit_length / d); } else { + /* Either the vector is small or one of it's values contained `nan`. */ zero_v2(r); d = 0.0f; } @@ -1162,17 +1179,20 @@ MINLINE float normalize_v2_length(float n[2], const float unit_length) return normalize_v2_v2_length(n, n, unit_length); } +/** + * \note any vectors containing `nan` will be zeroed out. + */ MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_length) { float d = dot_v3v3(a, a); - /* a larger value causes normalize errors in a - * scaled down models with camera extreme close */ + /* A larger value causes normalize errors in a scaled down models with camera extreme close. */ if (d > 1.0e-35f) { d = sqrtf(d); mul_v3_v3fl(r, a, unit_length / d); } else { + /* Either the vector is small or one of it's values contained `nan`. */ zero_v3(r); d = 0.0f; } diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4d0dc43ed1e..4d0678035ba 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1102,12 +1102,9 @@ bool BLI_path_abs(char *path, const char *basepath) } #ifdef WIN32 - /* skip first two chars, which in case of - * absolute path will be drive:/blabla and - * in case of relpath //blabla/. So relpath - * // will be retained, rest will be nice and - * shiny win32 backward slashes :) -jesterKing - */ + /* NOTE(@jesterking): Skip first two chars, which in case of absolute path will + * be `drive:/blabla` and in case of `relpath` `//blabla/`. + * So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */ BLI_str_replace_char(path + 2, '/', '\\'); #endif @@ -1897,7 +1894,7 @@ bool BLI_path_name_at_index(const char *__restrict path, if (index_step == index) { *r_offset = prev; *r_len = i - prev; - /* printf("!!! %d %d\n", start, end); */ + // printf("!!! %d %d\n", start, end); return true; } index_step += 1; diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c index 7781e3a0f6f..ed07b002e32 100644 --- a/source/blender/blenlib/intern/polyfill_2d_beautify.c +++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c @@ -25,7 +25,7 @@ * on a simple polygon representation where we _know_: * * - The polygon is primitive with no holes with a continuous boundary. - * - Tris have consistent winding. + * - Triangles have consistent winding. * - 2d (saves some hassles projecting face pairs on an axis for every edge-rotation) * also saves us having to store all previous edge-states (see #EdRotState in bmesh_beautify.c) * diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index b0d00007580..f0cf19bf508 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -430,7 +430,7 @@ static void testvertexnearedge(ScanFillContext *sf_ctx) /* new edge */ ed1 = BLI_scanfill_edge_add(sf_ctx, eed->v1, eve); - /* printf("fill: vertex near edge %x\n", eve); */ + // printf("fill: vertex near edge %x\n", eve); ed1->poly_nr = eed->poly_nr; eed->v1 = eve; eve->edge_tot = 3; @@ -608,7 +608,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl sc = scdata; for (a = 0; a < verts; a++) { - /* printf("VERTEX %d index %d\n", a, sc->vert->tmp.u); */ + // printf("VERTEX %d index %d\n", a, sc->vert->tmp.u); /* Set connect-flags. */ for (ed1 = sc->edge_first; ed1; ed1 = eed_next) { eed_next = ed1->next; @@ -634,13 +634,13 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl * (and doesn't work during grab). */ /* if (callLocalInterruptCallBack()) break; */ if (totface >= maxface) { - /* printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts); */ + // printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts); a = verts; break; } if (ed2 == NULL) { sc->edge_first = sc->edge_last = NULL; - /* printf("just 1 edge to vert\n"); */ + // printf("just 1 edge to vert\n"); BLI_addtail(&sf_ctx->filledgebase, ed1); ed1->v2->f = SF_VERT_NEW; ed1->v1->edge_tot--; @@ -662,7 +662,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl break; } - /* printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */ + // printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); miny = min_ff(v1->xy[1], v3->xy[1]); sc1 = sc + 1; @@ -705,7 +705,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl if (best_sc) { /* make new edge, and start over */ - /* printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u); */ + // printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u); ed3 = BLI_scanfill_edge_add(sf_ctx, v2, best_sc->vert); BLI_remlink(&sf_ctx->filledgebase, ed3); @@ -717,7 +717,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl } else { /* new triangle */ - /* printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */ + // printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); addfillface(sf_ctx, v1, v2, v3); totface++; BLI_remlink((ListBase *)&(sc->edge_first), ed1); @@ -741,11 +741,11 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl ed3->v1->edge_tot++; ed3->v2->edge_tot++; - /* printf("add new edge %x %x\n", v1, v3); */ + // printf("add new edge %x %x\n", v1, v3); sc1 = addedgetoscanlist(scdata, ed3, verts); if (sc1) { /* ed3 already exists: remove if a boundary */ - /* printf("Edge exists\n"); */ + // printf("Edge exists\n"); ed3->v1->edge_tot--; ed3->v2->edge_tot--; @@ -954,7 +954,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const poly++; } } - /* printf("amount of poly's: %d\n", poly); */ + // printf("amount of poly's: %d\n", poly); } else if (poly) { /* we pre-calculated poly_nr */ @@ -1020,7 +1020,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const } } if (BLI_listbase_is_empty(&sf_ctx->filledgebase)) { - /* printf("All edges removed\n"); */ + // printf("All edges removed\n"); return 0; } } diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 19b925535e2..47bb2f0e8dd 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -114,7 +114,7 @@ double BLI_dir_free_space(const char *dir) tmp[0] = '\\'; tmp[1] = 0; /* Just a fail-safe. */ - if (ELEM(dir[0] == '/', '\\')) { + if (ELEM(dir[0], '/', '\\')) { tmp[0] = '\\'; tmp[1] = 0; } diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc index 8bbd109fb81..89aeccdc105 100644 --- a/source/blender/blenlib/tests/BLI_array_store_test.cc +++ b/source/blender/blenlib/tests/BLI_array_store_test.cc @@ -187,17 +187,6 @@ static void testbuffer_list_state_from_data__stride_expand(ListBase *lb, ((void)0) /* test in both directions */ -#define TESTBUFFER_STRINGS_EX(bs, ...) \ - { \ - ListBase lb; \ - TESTBUFFER_STRINGS_CREATE(&lb, __VA_ARGS__); \ -\ - testbuffer_run_tests(bs, &lb); \ -\ - testbuffer_list_free(&lb); \ - } \ - ((void)0) - #define TESTBUFFER_STRINGS(stride, chunk_count, ...) \ { \ ListBase lb; \ diff --git a/source/blender/blenlib/tests/BLI_math_vector_test.cc b/source/blender/blenlib/tests/BLI_math_vector_test.cc index 7e75a521d4c..955f02b8065 100644 --- a/source/blender/blenlib/tests/BLI_math_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_math_vector_test.cc @@ -45,3 +45,21 @@ TEST(math_vector, ClampVecWithVecs) EXPECT_FLOAT_EQ(1.0f, c[0]); EXPECT_FLOAT_EQ(3.0f, c[1]); } + +TEST(math_vector, test_invert_v3_safe) +{ + float v3_with_zeroes[3] = {0.0f, 2.0f, 3.0f}; + invert_v3_safe(v3_with_zeroes); + EXPECT_FLOAT_EQ(0.0f, v3_with_zeroes[0]); + EXPECT_FLOAT_EQ(0.5f, v3_with_zeroes[1]); + EXPECT_FLOAT_EQ(0.33333333333f, v3_with_zeroes[2]); + + float v3_without_zeroes[3] = {1.0f, 2.0f, 3.0f}; + float inverted_unsafe[3] = {1.0f, 2.0f, 3.0f}; + invert_v3_safe(v3_without_zeroes); + invert_v3(inverted_unsafe); + + EXPECT_FLOAT_EQ(inverted_unsafe[0], v3_without_zeroes[0]); + EXPECT_FLOAT_EQ(inverted_unsafe[1], v3_without_zeroes[1]); + EXPECT_FLOAT_EQ(inverted_unsafe[2], v3_without_zeroes[2]); +} diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 04e13fbd1d6..dbdb181281a 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -108,6 +108,9 @@ typedef struct BlendFileReadReport { * during this file read. */ int missing_libraries; int missing_linked_id; + /* Some sub-categories of the above `missing_linked_id` counter. */ + int missing_obdata; + int missing_obproxies; /* Number of root override IDs that were resynced. */ int resynced_lib_overrides; } count; @@ -115,6 +118,7 @@ typedef struct BlendFileReadReport { /* Number of libraries which had overrides that needed to be resynced, and a single linked list * of those. */ int resynced_lib_overrides_libraries_count; + bool do_resynced_lib_overrides_libraries_list; struct LinkNode *resynced_lib_overrides_libraries; } BlendFileReadReport; diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h index fc41a6e832f..4e240e2462b 100644 --- a/source/blender/blenloader/BLO_undofile.h +++ b/source/blender/blenloader/BLO_undofile.h @@ -24,6 +24,8 @@ * \ingroup blenloader */ +#include "BLI_filereader.h" + struct GHash; struct Scene; @@ -65,6 +67,16 @@ typedef struct MemFileUndoData { size_t undo_size; } MemFileUndoData; +/* FileReader-compatible wrapper for reading MemFiles */ +typedef struct { + FileReader reader; + + MemFile *memfile; + int undo_direction; + + bool memchunk_identical; +} UndoReader; + /* actually only used writefile.c */ void BLO_memfile_write_init(MemFileWriteData *mem_data, @@ -84,3 +96,5 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene); extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename); + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction); diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index f5baf0dcb83..89631588ed0 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -42,7 +42,7 @@ set(INC ) set(INC_SYS - ${ZLIB_INCLUDE_DIRS} + ${ZSTD_INCLUDE_DIRS} ) set(SRC diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e48c305fc4b..49c3497f996 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -21,8 +21,6 @@ * \ingroup blenloader */ -#include "zlib.h" - #include <ctype.h> /* for isdigit. */ #include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */ #include <limits.h> @@ -71,7 +69,6 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" -#include "BLI_mmap.h" #include "BLI_threads.h" #include "PIL_time.h" @@ -788,7 +785,7 @@ static BHeadN *get_bhead(FileData *fd) */ if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) { bhead4.code = DATA; - readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL); + readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4)); if (readsize == sizeof(bhead4) || bhead4.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -811,7 +808,7 @@ static BHeadN *get_bhead(FileData *fd) } else { bhead8.code = DATA; - readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL); + readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8)); if (readsize == sizeof(bhead8) || bhead8.code == ENDB) { if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { @@ -845,22 +842,22 @@ static BHeadN *get_bhead(FileData *fd) /* pass */ } #ifdef USE_BHEAD_READ_ON_DEMAND - else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { + else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) { /* Delay reading bhead content. */ new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead"); if (new_bhead) { new_bhead->next = new_bhead->prev = NULL; - new_bhead->file_offset = fd->file_offset; + new_bhead->file_offset = fd->file->offset; new_bhead->has_data = false; new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR); + off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR); if (seek_new == -1) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } - BLI_assert(fd->file_offset == seek_new); + BLI_assert(fd->file->offset == seek_new); } else { fd->is_eof = true; @@ -878,14 +875,17 @@ static BHeadN *get_bhead(FileData *fd) new_bhead->is_memchunk_identical = false; new_bhead->bhead = bhead; - readsize = fd->read( - fd, new_bhead + 1, (size_t)bhead.len, &new_bhead->is_memchunk_identical); + readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len); - if (readsize != (ssize_t)bhead.len) { + if (readsize != bhead.len) { fd->is_eof = true; MEM_freeN(new_bhead); new_bhead = NULL; } + + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } else { fd->is_eof = true; @@ -964,17 +964,19 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf) bool success = true; BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock); BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0); - off64_t offset_backup = fd->file_offset; - if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) { + off64_t offset_backup = fd->file->offset; + if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) { success = false; } else { - if (fd->read(fd, buf, (size_t)new_bhead->bhead.len, &new_bhead->is_memchunk_identical) != - (ssize_t)new_bhead->bhead.len) { + if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) { success = false; } + if (fd->flags & FD_FLAGS_IS_MEMFILE) { + new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical; + } } - if (fd->seek(fd, offset_backup, SEEK_SET) == -1) { + if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) { success = false; } return success; @@ -1017,7 +1019,7 @@ static void decode_blender_header(FileData *fd) ssize_t readsize; /* read in the header data */ - readsize = fd->read(fd, header, sizeof(header), NULL); + readsize = fd->file->read(fd->file, header, sizeof(header)); if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') && ELEM(header[8], 'v', 'V') && @@ -1147,210 +1149,12 @@ static int *read_file_thumbnail(FileData *fd) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name File Data API - * \{ */ - -/* Regular file reading. */ - -static ssize_t fd_read_data_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - ssize_t readsize = read(filedata->filedes, buffer, size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int whence) -{ - filedata->file_offset = BLI_lseek(filedata->filedes, offset, whence); - return filedata->file_offset; -} - -/* GZip file reading. */ - -static ssize_t fd_read_gzip_from_file(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - BLI_assert(size <= INT_MAX); - - ssize_t readsize = gzread(filedata->gzfiledes, buffer, (uint)size); - - if (readsize < 0) { - readsize = EOF; - } - else { - filedata->file_offset += readsize; - } - - return readsize; -} - -/* Memory reading. */ - -static ssize_t fd_read_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - ssize_t readsize = (ssize_t)MIN2(size, filedata->buffersize - (size_t)filedata->file_offset); - - memcpy(buffer, filedata->buffer + filedata->file_offset, (size_t)readsize); - filedata->file_offset += readsize; - - return readsize; -} - -/* Memory-mapped file reading. - * By using mmap(), we can map a file so that it can be treated like normal memory, - * meaning that we can just read from it with memcpy() etc. - * This avoids system call overhead and can significantly speed up file loading. - */ - -static ssize_t fd_read_from_mmap(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - /* don't read more bytes than there are available in the buffer */ - size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset)); - - if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) { - return 0; - } - - filedata->file_offset += readsize; - - return readsize; -} - -static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence) -{ - off64_t new_pos; - if (whence == SEEK_CUR) { - new_pos = filedata->file_offset + offset; - } - else if (whence == SEEK_SET) { - new_pos = offset; - } - else if (whence == SEEK_END) { - new_pos = filedata->buffersize + offset; - } - else { - return -1; - } - - if (new_pos < 0 || new_pos > filedata->buffersize) { - return -1; - } - - filedata->file_offset = new_pos; - return filedata->file_offset; -} - -/* MemFile reading. */ - -static ssize_t fd_read_from_memfile(FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunck_identical) -{ - static size_t seek = SIZE_MAX; /* the current position */ - static size_t offset = 0; /* size of previous chunks */ - static MemFileChunk *chunk = NULL; - size_t chunkoffset, readsize, totread; - - if (r_is_memchunck_identical != NULL) { - *r_is_memchunck_identical = true; - } - - if (size == 0) { - return 0; - } - - if (seek != (size_t)filedata->file_offset) { - chunk = filedata->memfile->chunks.first; - seek = 0; - - while (chunk) { - if (seek + chunk->size > (size_t)filedata->file_offset) { - break; - } - seek += chunk->size; - chunk = chunk->next; - } - offset = seek; - seek = (size_t)filedata->file_offset; - } - - if (chunk) { - totread = 0; - - do { - /* first check if it's on the end if current chunk */ - if (seek - offset == chunk->size) { - offset += chunk->size; - chunk = chunk->next; - } - - /* debug, should never happen */ - if (chunk == NULL) { - CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk"); - return 0; - } - - chunkoffset = seek - offset; - readsize = size - totread; - - /* data can be spread over multiple chunks, so clamp size - * to within this chunk, and then it will read further in - * the next chunk */ - if (chunkoffset + readsize > chunk->size) { - readsize = chunk->size - chunkoffset; - } - - memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); - totread += readsize; - filedata->file_offset += readsize; - seek += readsize; - if (r_is_memchunck_identical != NULL) { - /* `is_identical` of current chunk represents whether it changed compared to previous undo - * step. this is fine in redo case, but not in undo case, where we need an extra flag - * defined when saving the next (future) step after the one we want to restore, as we are - * supposed to 'come from' that future undo step, and not the one before current one. */ - *r_is_memchunck_identical &= filedata->undo_direction == STEP_REDO ? - chunk->is_identical : - chunk->is_identical_future; - } - } while (totread < size); - - return (ssize_t)totread; - } - - return 0; -} - static FileData *filedata_new(BlendFileReadReport *reports) { BLI_assert(reports != NULL); FileData *fd = MEM_callocN(sizeof(FileData), "FileData"); - fd->filedes = -1; - fd->gzfiledes = NULL; - fd->memsdna = DNA_sdna_current_get(); fd->datamap = oldnewmap_new(); @@ -1387,78 +1191,66 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) static FileData *blo_filedata_from_file_descriptor(const char *filepath, BlendFileReadReport *reports, - int file) + int filedes) { - FileDataReadFn *read_fn = NULL; - FileDataSeekFn *seek_fn = NULL; /* Optional. */ - size_t buffersize = 0; - BLI_mmap_file *mmap_file = NULL; - - gzFile gzfile = (gzFile)Z_NULL; - char header[7]; + FileReader *rawfile = BLI_filereader_new_file(filedes); + FileReader *file = NULL; - /* Regular file. */ errno = 0; - if (read(file, header, sizeof(header)) != sizeof(header)) { + /* If opening the file failed or we can't read the header, give up. */ + if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { BKE_reportf(reports->reports, RPT_WARNING, "Unable to read '%s': %s", filepath, errno ? strerror(errno) : TIP_("insufficient content")); + if (rawfile) { + rawfile->close(rawfile); + } + else { + close(filedes); + } return NULL; } - /* Regular file. */ - if (memcmp(header, "BLENDER", sizeof(header)) == 0) { - read_fn = fd_read_data_from_file; - seek_fn = fd_seek_data_from_file; + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); - mmap_file = BLI_mmap_open(file); - if (mmap_file != NULL) { - read_fn = fd_read_from_mmap; - seek_fn = fd_seek_from_mmap; - buffersize = BLI_lseek(file, 0, SEEK_END); + /* Check if we have a regular file. */ + if (memcmp(header, "BLENDER", sizeof(header)) == 0) { + /* Try opening the file with memory-mapped IO. */ + file = BLI_filereader_new_mmap(filedes); + if (file == NULL) { + /* mmap failed, so just keep using rawfile. */ + file = rawfile; + rawfile = NULL; } } - - BLI_lseek(file, 0, SEEK_SET); - - /* Gzip file. */ - errno = 0; - if ((read_fn == NULL) && - /* Check header magic. */ - (header[0] == 0x1f && header[1] == 0x8b)) { - gzfile = BLI_gzopen(filepath, "rb"); - if (gzfile == (gzFile)Z_NULL) { - BKE_reportf(reports->reports, - RPT_WARNING, - "Unable to open '%s': %s", - filepath, - errno ? strerror(errno) : TIP_("unknown error reading file")); - return NULL; + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ + } + } + else if (BLI_file_magic_is_zstd(header)) { + file = BLI_filereader_new_zstd(rawfile); + if (file != NULL) { + rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */ } - - /* 'seek_fn' is too slow for gzip, don't set it. */ - read_fn = fd_read_gzip_from_file; - /* Caller must close. */ - file = -1; } - if (read_fn == NULL) { + /* Clean up `rawfile` if it wasn't taken over. */ + if (rawfile != NULL) { + rawfile->close(rawfile); + } + if (file == NULL) { BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath); return NULL; } FileData *fd = filedata_new(reports); - - fd->filedes = file; - fd->gzfiledes = gzfile; - - fd->read = read_fn; - fd->seek = seek_fn; - fd->mmap_file = mmap_file; - fd->buffersize = buffersize; + fd->file = file; return fd; } @@ -1475,11 +1267,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead errno ? strerror(errno) : TIP_("unknown error reading file")); return NULL; } - FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file); - if ((fd == NULL) || (fd->filedes == -1)) { - close(file); - } - return fd; + return blo_filedata_from_file_descriptor(filepath, reports, file); } /* cannot be called with relative paths anymore! */ @@ -1513,50 +1301,6 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath) return NULL; } -static ssize_t fd_read_gzip_from_memory(FileData *filedata, - void *buffer, - size_t size, - bool *UNUSED(r_is_memchunck_identical)) -{ - int err; - - filedata->strm.next_out = (Bytef *)buffer; - filedata->strm.avail_out = (uint)size; - - /* Inflate another chunk. */ - err = inflate(&filedata->strm, Z_SYNC_FLUSH); - - if (err == Z_STREAM_END) { - return 0; - } - if (err != Z_OK) { - CLOG_ERROR(&LOG, "ZLib error (code %d)", err); - return 0; - } - - filedata->file_offset += size; - - return (ssize_t)size; -} - -static int fd_read_gzip_from_memory_init(FileData *fd) -{ - - fd->strm.next_in = (Bytef *)fd->buffer; - fd->strm.avail_in = fd->buffersize; - fd->strm.total_out = 0; - fd->strm.zalloc = Z_NULL; - fd->strm.zfree = Z_NULL; - - if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK) { - return 0; - } - - fd->read = fd_read_gzip_from_memory; - - return 1; -} - FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports) { if (!mem || memsize < SIZEOFBLENDERHEADER) { @@ -1565,24 +1309,24 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe return NULL; } - FileData *fd = filedata_new(reports); - const char *cp = mem; - - fd->buffer = mem; - fd->buffersize = memsize; + FileReader *mem_file = BLI_filereader_new_memory(mem, memsize); + FileReader *file = mem_file; - /* test if gzip */ - if (cp[0] == 0x1f && cp[1] == 0x8b) { - if (0 == fd_read_gzip_from_memory_init(fd)) { - blo_filedata_free(fd); - return NULL; - } + if (BLI_file_magic_is_gzip(mem)) { + file = BLI_filereader_new_gzip(mem_file); } - else { - fd->read = fd_read_from_memory; + else if (BLI_file_magic_is_zstd(mem)) { + file = BLI_filereader_new_zstd(mem_file); } - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + if (file == NULL) { + /* Compression initialization failed. */ + mem_file->close(mem_file); + return NULL; + } + + FileData *fd = filedata_new(reports); + fd->file = file; return blo_decode_and_check(fd, reports->reports); } @@ -1597,11 +1341,9 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, } FileData *fd = filedata_new(reports); - fd->memfile = memfile; + fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction); fd->undo_direction = params->undo_direction; - - fd->read = fd_read_from_memfile; - fd->flags |= FD_FLAGS_NOT_MY_BUFFER; + fd->flags |= FD_FLAGS_IS_MEMFILE; return blo_decode_and_check(fd, reports->reports); } @@ -1609,30 +1351,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, void blo_filedata_free(FileData *fd) { if (fd) { - if (fd->filedes != -1) { - close(fd->filedes); - } - - if (fd->gzfiledes != NULL) { - gzclose(fd->gzfiledes); - } - - if (fd->strm.next_in) { - int err = inflateEnd(&fd->strm); - if (err != Z_OK) { - CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err); - } - } - - if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) { - MEM_freeN((void *)fd->buffer); - fd->buffer = NULL; - } - - if (fd->mmap_file) { - BLI_mmap_free(fd->mmap_file); - fd->mmap_file = NULL; - } + fd->file->close(fd->file); /* Free all BHeadN data blocks */ #ifndef NDEBUG @@ -1640,7 +1359,7 @@ void blo_filedata_free(FileData *fd) #else /* Sanity check we're not keeping memory we don't need. */ LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) { - if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { + if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) { BLI_assert(new_bhead->has_data == 0); } MEM_freeN(new_bhead); @@ -2096,7 +1815,7 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), void blo_cache_storage_init(FileData *fd, Main *bmain) { - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { BLI_assert(fd->cache_storage == NULL); fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__); fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2261,7 +1980,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname) * undo since DNA must match. */ static const void *peek_struct_undo(FileData *fd, BHead *bhead) { - BLI_assert(fd->memfile != NULL); + BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE); UNUSED_VARS_NDEBUG(fd); return (bhead->len) ? (const void *)(bhead + 1) : NULL; } @@ -3679,7 +3398,7 @@ static BHead *read_libblock(FileData *fd, * When datablocks are changed but still exist, we restore them at the old * address and inherit recalc flags for the dependency graph. */ ID *id_old = NULL; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) { if (r_id) { *r_id = id_old; @@ -3980,13 +3699,14 @@ static void lib_link_all(FileData *fd, Main *bmain) continue; } - if (fd->memfile != NULL && GS(id->name) == ID_WM) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) { /* No load UI for undo memfiles. * Only WM currently, SCR needs it still (see below), and so does WS? */ continue; } - if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo && + (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) { /* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across * current undo step, and old IDs re-use their old memory address, we do not need to liblink * it at all. */ @@ -4165,7 +3885,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) BlendFileData *bfd; ListBase mainlist = {NULL, NULL}; - if (fd->memfile != NULL) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step"); } @@ -4256,7 +3976,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) } /* do before read_libraries, but skip undo case */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) { do_versions(fd, NULL, bfd->main); } @@ -4278,7 +3998,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries; /* Skip in undo case. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Note that we can't recompute user-counts at this point in undo case, we play too much with * IDs from different memory realms, and Main database is not in a fully valid state yet. */ @@ -4311,7 +4031,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) /* Now that all our data-blocks are loaded, * we can re-generate overrides from their references. */ - if (fd->memfile == NULL) { + if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) { /* Do not apply in undo case! */ fd->reports->duration.lib_overrides = PIL_check_seconds_timer(); @@ -4391,7 +4111,7 @@ static void sort_bhead_old_map(FileData *fd) static BHead *find_previous_lib(FileData *fd, BHead *bhead) { /* Skip library data-blocks in undo, see comment in read_libblock. */ - if (fd->memfile) { + if (fd->flags & FD_FLAGS_IS_MEMFILE) { return NULL; } @@ -5850,7 +5570,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p) bool BLO_read_data_is_undo(BlendDataReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr) @@ -5870,7 +5590,7 @@ BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader) bool BLO_read_lib_is_undo(BlendLibReader *reader) { - return reader->fd->memfile != NULL; + return (reader->fd->flags & FD_FLAGS_IS_MEMFILE); } Main *BLO_read_lib_get_main(BlendLibReader *reader) diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index b04043f9641..beeed8e45ae 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -28,10 +28,10 @@ # include "BLI_winstuff.h" #endif +#include "BLI_filereader.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" /* for ReportType */ -#include "zlib.h" struct BLI_mmap_file; struct BLOCacheStorage; @@ -50,7 +50,7 @@ enum eFileDataFlag { FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1, FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2, FD_FLAGS_FILE_OK = 1 << 3, - FD_FLAGS_NOT_MY_BUFFER = 1 << 4, + FD_FLAGS_IS_MEMFILE = 1 << 4, /* XXX Unused in practice (checked once but never set). */ FD_FLAGS_NOT_MY_LIBMAP = 1 << 5, }; @@ -60,44 +60,18 @@ enum eFileDataFlag { # pragma GCC poison off_t #endif -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__) -typedef int64_t off64_t; -#endif - -typedef ssize_t(FileDataReadFn)(struct FileData *filedata, - void *buffer, - size_t size, - bool *r_is_memchunk_identical); -typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence); - typedef struct FileData { /** Linked list of BHeadN's. */ ListBase bhead_list; enum eFileDataFlag flags; bool is_eof; - size_t buffersize; - off64_t file_offset; - FileDataReadFn *read; - FileDataSeekFn *seek; + FileReader *file; - /** Regular file reading. */ - int filedes; - - /** Variables needed for reading from memory / stream / memory-mapped files. */ - const char *buffer; - struct BLI_mmap_file *mmap_file; - /** Variables needed for reading from memfile (undo). */ - struct MemFile *memfile; /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use * to detect unchanged data from memfile. */ int undo_direction; /* eUndoStepDir */ - /** Variables needed for reading from file. */ - gzFile gzfiledes; - /** Gzip stream for memory decompression. */ - z_stream strm; - /** Now only in use for library appending. */ char relabase[FILE_MAX]; diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c index 2eeeac2e8d7..62072cf7df5 100644 --- a/source/blender/blenloader/intern/undofile.c +++ b/source/blender/blenloader/intern/undofile.c @@ -48,6 +48,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_undo_system.h" /* keep last */ #include "BLI_strict_flags.h" @@ -273,3 +274,97 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename) } return true; } + +static ssize_t undo_read(FileReader *reader, void *buffer, size_t size) +{ + UndoReader *undo = (UndoReader *)reader; + + static size_t seek = SIZE_MAX; /* The current position. */ + static size_t offset = 0; /* Size of previous chunks. */ + static MemFileChunk *chunk = NULL; + size_t chunkoffset, readsize, totread; + + undo->memchunk_identical = true; + + if (size == 0) { + return 0; + } + + if (seek != (size_t)undo->reader.offset) { + chunk = undo->memfile->chunks.first; + seek = 0; + + while (chunk) { + if (seek + chunk->size > (size_t)undo->reader.offset) { + break; + } + seek += chunk->size; + chunk = chunk->next; + } + offset = seek; + seek = (size_t)undo->reader.offset; + } + + if (chunk) { + totread = 0; + + do { + /* First check if it's on the end if current chunk. */ + if (seek - offset == chunk->size) { + offset += chunk->size; + chunk = chunk->next; + } + + /* Debug, should never happen. */ + if (chunk == NULL) { + printf("illegal read, chunk zero\n"); + return 0; + } + + chunkoffset = seek - offset; + readsize = size - totread; + + /* Data can be spread over multiple chunks, so clamp size + * to within this chunk, and then it will read further in + * the next chunk. */ + if (chunkoffset + readsize > chunk->size) { + readsize = chunk->size - chunkoffset; + } + + memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize); + totread += readsize; + undo->reader.offset += (off64_t)readsize; + seek += readsize; + + /* `is_identical` of current chunk represents whether it changed compared to previous undo + * step. this is fine in redo case, but not in undo case, where we need an extra flag + * defined when saving the next (future) step after the one we want to restore, as we are + * supposed to 'come from' that future undo step, and not the one before current one. */ + undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical : + chunk->is_identical_future; + } while (totread < size); + + return (ssize_t)totread; + } + + return 0; +} + +static void undo_close(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction) +{ + UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__); + + undo->memfile = memfile; + undo->undo_direction = undo_direction; + + undo->reader.read = undo_read; + undo->reader.seek = NULL; + undo->reader.close = undo_close; + + return (FileReader *)undo; +} diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index e56c1995363..436645c2241 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -23,8 +23,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include <io.h> /* for open close read */ -# include <zlib.h> /* odd include order-issue */ +# include <io.h> /* for open close read */ #endif /* allow readfile to use deprecated functionality */ diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 858f5d85a90..7c644fa3b55 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -2390,7 +2390,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (md = ob->modifiers.first; md; md = md->next) { if (md->type == eModifierType_Triangulate) { TriangulateModifierData *tmd = (TriangulateModifierData *)md; - if ((tmd->flag & MOD_TRIANGULATE_BEAUTY)) { + if (tmd->flag & MOD_TRIANGULATE_BEAUTY) { tmd->quad_method = MOD_TRIANGULATE_QUAD_BEAUTY; tmd->ngon_method = MOD_TRIANGULATE_NGON_BEAUTY; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index af05c4b902f..9d65488e8d4 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -111,40 +111,13 @@ #include "BLO_readfile.h" #include "readfile.h" +#include "versioning_common.h" + #include "MEM_guardedalloc.h" /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) -/** - * Rename if the ID doesn't exist. - */ -static ID *rename_id_for_versioning(Main *bmain, - const short id_type, - const char *name_src, - const char *name_dst) -{ - /* We can ignore libraries */ - ListBase *lb = which_libbase(bmain, id_type); - ID *id = NULL; - LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == NULL) { - if (STREQ(idtest->name + 2, name_src)) { - id = idtest; - } - if (STREQ(idtest->name + 2, name_dst)) { - return NULL; - } - } - } - if (id != NULL) { - BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); - /* We know it's unique, this just sorts. */ - BLI_libblock_ensure_unique_name(bmain, id->name); - } - return id; -} - static bScreen *screen_parent_find(const bScreen *screen) { /* Can avoid lookup if screen state isn't maximized/full @@ -348,7 +321,7 @@ static void do_version_layer_collection_post(ViewLayer *view_layer, lc->flag |= LAYER_COLLECTION_EXCLUDE; } if (enabled && !selectable) { - lc->collection->flag |= COLLECTION_RESTRICT_SELECT; + lc->collection->flag |= COLLECTION_HIDE_SELECT; } } @@ -477,7 +450,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene) collections[layer] = collection; if (!(scene->lay & (1 << layer))) { - collection->flag |= COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER; + collection->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } } @@ -1225,7 +1198,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) /* Add fake user for all existing groups. */ id_fake_user_set(&collection->id); - if (collection->flag & (COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER)) { + if (collection->flag & (COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER)) { continue; } @@ -1256,8 +1229,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) char name[MAX_ID_NAME]; BLI_snprintf(name, sizeof(name), DATA_("Hidden %d"), coll_idx + 1); *collection_hidden = BKE_collection_add(bmain, collection, name); - (*collection_hidden)->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + (*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } BKE_collection_object_add(bmain, *collection_hidden, ob); @@ -1679,32 +1651,32 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) Brush *brush; Material *ma; /* Pen Soft brush. */ - brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); if (brush) { brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; } - rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); - rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); - rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); - rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); - rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); - rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); + do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); + do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); + do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold"); + do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); ma = BLI_findstring(&bmain->materials, "Black", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke"); } ma = BLI_findstring(&bmain->materials, "Red", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); + do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke"); } ma = BLI_findstring(&bmain->materials, "Grey", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); + do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill"); } ma = BLI_findstring(&bmain->materials, "Black Dots", offsetof(ID, name) + 2); if (ma && ma->gp_style) { - rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke"); } brush = BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2); @@ -4110,9 +4082,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 280, 75)) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { if (scene->master_collection != NULL) { - scene->master_collection->flag &= ~(COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_SELECT | - COLLECTION_RESTRICT_RENDER); + scene->master_collection->flag &= ~(COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_SELECT | + COLLECTION_HIDE_RENDER); } UnitSettings *unit = &scene->unit; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 09d43676b8f..7f7a2d97cbb 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -170,7 +170,7 @@ static void seq_convert_transform_crop(const Scene *scene, int image_size_x = scene->r.xsch; int image_size_y = scene->r.ysch; - /* Hardcoded legacy bit-flags which has been removed. */ + /* Hard-coded legacy bit-flags which has been removed. */ const uint32_t use_transform_flag = (1 << 16); const uint32_t use_crop_flag = (1 << 17); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 9aec18ea279..eb01bfbfb9c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,7 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -29,6 +30,7 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_collection_types.h" +#include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_genfile.h" #include "DNA_listBase.h" @@ -383,6 +385,19 @@ static void do_version_bones_bbone_len_scale(ListBase *lb) } } +static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb) +{ + /* Binding array data could be freed without properly resetting its size data. */ + LISTBASE_FOREACH (bConstraint *, con, lb) { + if (con->type == CONSTRAINT_TYPE_SPLINEIK) { + bSplineIKConstraint *data = (bSplineIKConstraint *)con->data; + if (data->points == NULL) { + data->numpoints = 0; + } + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -643,12 +658,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!DNA_struct_elem_find( fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) { LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { - BKE_asset_library_reference_init_default(&workspace->asset_library); + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); } } if (!DNA_struct_elem_find( - fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library")) { + fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { @@ -657,7 +672,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) { continue; } - BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library); + BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref); } } } @@ -691,6 +706,76 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + /* Font names were copied directly into ID names, see: T90417. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) { + ListBase *lb = which_libbase(bmain, ID_VF); + BKE_main_id_repair_duplicate_names_listbase(lb); + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) { + if (!DNA_struct_elem_find( + fd->filesdna, "View3DOverlay", "float", "normals_constant_screen_size")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.normals_constant_screen_size = 7.0f; + } + } + } + } + } + + /* Fix SplineIK constraint's inconsistency between binding points array and its stored size. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + /* NOTE: Objects should never have SplineIK constraint, so no need to apply this fix on + * their constraints. */ + if (ob->pose) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + do_version_constraints_spline_ik_joint_bindings(&pchan->constraints); + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 18)) { + if (!DNA_struct_elem_find( + fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library_ref")) { + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + BKE_asset_library_reference_init_default(&workspace->asset_library_ref); + } + } + + if (!DNA_struct_elem_find( + fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype != SPACE_FILE) { + continue; + } + + SpaceFile *sfile = (SpaceFile *)space; + if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) { + continue; + } + BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref); + } + } + } + } + + /* Previously, only text ending with `.py` would run, apply this logic + * to existing files so text that happens to have the "Register" enabled + * doesn't suddenly start running code on startup that was previously ignored. */ + LISTBASE_FOREACH (Text *, text, &bmain->texts) { + if ((text->flags & TXT_ISSCRIPT) && !BLI_path_extension_check(text->id.name + 2, ".py")) { + text->flags &= ~TXT_ISSCRIPT; + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -702,5 +787,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Add node storage for subdivision surface node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_SUBDIVISION_SURFACE) { + if (node->storage == NULL) { + NodeGeometrySubdivisionSurface *data = MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; + } + } + } + } + } + FOREACH_NODETREE_END; } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index f5083b8e259..208c02b60d1 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -20,9 +20,15 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include <cstring> + #include "DNA_screen_types.h" #include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BKE_lib_id.h" +#include "BKE_main.h" #include "MEM_guardedalloc.h" @@ -48,3 +54,34 @@ ARegion *do_versions_add_region_if_not_found(ListBase *regionbase, BLI_insertlinkafter(regionbase, link_after_region, new_region); return new_region; } + +/** + * Rename if the ID doesn't exist. + * + * \return the ID (if found). + */ +ID *do_versions_rename_id(Main *bmain, + const short id_type, + const char *name_src, + const char *name_dst) +{ + /* We can ignore libraries */ + ListBase *lb = which_libbase(bmain, id_type); + ID *id = nullptr; + LISTBASE_FOREACH (ID *, idtest, lb) { + if (idtest->lib == nullptr) { + if (STREQ(idtest->name + 2, name_src)) { + id = idtest; + } + if (STREQ(idtest->name + 2, name_dst)) { + return nullptr; + } + } + } + if (id != nullptr) { + BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); + /* We know it's unique, this just sorts. */ + BLI_libblock_ensure_unique_name(bmain, id->name); + } + return id; +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index a1769d4639e..47e0b74a3e4 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -22,6 +22,7 @@ struct ARegion; struct ListBase; +struct Main; #ifdef __cplusplus extern "C" { @@ -32,6 +33,11 @@ struct ARegion *do_versions_add_region_if_not_found(struct ListBase *regionbase, const char *name, int link_after_region_type); +ID *do_versions_rename_id(Main *bmain, + const short id_type, + const char *name_src, + const char *name_dst); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 94ee89c5120..90e6b43f02e 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -78,6 +78,12 @@ static IDProperty *cycles_properties_from_ID(ID *id) return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL; } +static IDProperty *cycles_visibility_properties_from_ID(ID *id) +{ + IDProperty *idprop = IDP_GetProperties(id, false); + return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : NULL; +} + static IDProperty *cycles_properties_from_view_layer(ViewLayer *view_layer) { IDProperty *idprop = view_layer->id_properties; @@ -1600,4 +1606,35 @@ void do_versions_after_linking_cycles(Main *bmain) } } } + + /* Move visibility from Cycles to Blender. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) { + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + IDProperty *cvisibility = cycles_visibility_properties_from_ID(&object->id); + int flag = 0; + + if (cvisibility) { + flag |= cycles_property_boolean(cvisibility, "camera", true) ? 0 : OB_HIDE_CAMERA; + flag |= cycles_property_boolean(cvisibility, "diffuse", true) ? 0 : OB_HIDE_DIFFUSE; + flag |= cycles_property_boolean(cvisibility, "glossy", true) ? 0 : OB_HIDE_GLOSSY; + flag |= cycles_property_boolean(cvisibility, "transmission", true) ? 0 : + OB_HIDE_TRANSMISSION; + flag |= cycles_property_boolean(cvisibility, "scatter", true) ? 0 : OB_HIDE_VOLUME_SCATTER; + flag |= cycles_property_boolean(cvisibility, "shadow", true) ? 0 : OB_HIDE_SHADOW; + } + + IDProperty *cobject = cycles_properties_from_ID(&object->id); + if (cobject) { + flag |= cycles_property_boolean(cobject, "is_holdout", false) ? OB_HOLDOUT : 0; + flag |= cycles_property_boolean(cobject, "is_shadow_catcher", false) ? OB_SHADOW_CATCHER : + 0; + } + + if (object->type == OB_LAMP) { + flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER; + } + + object->visibility_flag |= flag; + } + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 8362e001ea6..82c577d11a0 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -65,38 +65,11 @@ #include "BLO_readfile.h" +#include "versioning_common.h" + /* Make preferences read-only, use versioning_userdef.c. */ #define U (*((const UserDef *)&U)) -/** - * Rename if the ID doesn't exist. - */ -static ID *rename_id_for_versioning(Main *bmain, - const short id_type, - const char *name_src, - const char *name_dst) -{ - /* We can ignore libraries */ - ListBase *lb = which_libbase(bmain, id_type); - ID *id = NULL; - LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == NULL) { - if (STREQ(idtest->name + 2, name_src)) { - id = idtest; - } - if (STREQ(idtest->name + 2, name_dst)) { - return NULL; - } - } - } - if (id != NULL) { - BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2); - /* We know it's unique, this just sorts. */ - BLI_libblock_ensure_unique_name(bmain, id->name); - } - return id; -} - static bool blo_is_builtin_template(const char *app_template) { /* For all builtin templates shipped with Blender. */ @@ -217,6 +190,7 @@ static void blo_update_defaults_screen(bScreen *screen, } /* Disable Curve Normals. */ v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_CU_NORMALS; + v3d->overlay.normals_constant_screen_size = 7.0f; } else if (area->spacetype == SPACE_CLIP) { SpaceClip *sclip = area->spacedata.first; @@ -406,28 +380,28 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) Brush *brush; /* Pencil brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); + do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil"); /* Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen"); /* Pen Soft brush. */ - brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"); if (brush) { brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; } /* Ink Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); + do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen"); /* Ink Pen Rough brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); + do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); /* Marker Bold brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); + do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold"); /* Marker Chisel brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); + do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel"); /* Remove useless Fill Area.001 brush. */ brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2); @@ -438,10 +412,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Rename and fix materials and enable default object lights on. */ if (app_template && STREQ(app_template, "2D_Animation")) { Material *ma = NULL; - rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); - rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke"); + do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke"); + do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill"); + do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke"); /* Dots Stroke. */ ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); @@ -553,8 +527,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } /* Objects */ - rename_id_for_versioning(bmain, ID_OB, "Lamp", "Light"); - rename_id_for_versioning(bmain, ID_LA, "Lamp", "Light"); + do_versions_rename_id(bmain, ID_OB, "Lamp", "Light"); + do_versions_rename_id(bmain, ID_LA, "Lamp", "Light"); if (app_template && STREQ(app_template, "2D_Animation")) { for (Object *object = bmain->objects.first; object; object = object->id.next) { diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 95cfc9975d7..6ba27b6ee9e 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -28,8 +28,7 @@ #else # include "BLI_winstuff.h" # include "winsock2.h" -# include <io.h> /* for open close read */ -# include <zlib.h> /* odd include order-issue */ +# include <io.h> /* for open close read */ #endif /* allow readfile to use deprecated functionality */ @@ -1335,7 +1334,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) ME_OPT_EDGES = (1 << 8), }; - if ((me->flag & ME_SUBSURF)) { + if (me->flag & ME_SUBSURF) { SubsurfModifierData *smd = (SubsurfModifierData *)BKE_modifier_new( eModifierType_Subsurf); diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index c409f0a71fc..0042ff29dc2 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -873,13 +873,6 @@ void blo_do_versions_userdef(UserDef *userdef) } } - if (!USER_VERSION_ATLEAST(293, 2)) { - /* Enable asset browser features by default for alpha testing. - * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha - * builds. */ - userdef->experimental.use_asset_browser = true; - } - if (!USER_VERSION_ATLEAST(293, 12)) { if (userdef->gizmo_size_navigate_v3d == 0) { userdef->gizmo_size_navigate_v3d = 80; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 12839a155e4..6f43fbf1fa0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -78,12 +78,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <zlib.h> #ifdef WIN32 # include "BLI_winstuff.h" # include "winsock2.h" # include <io.h> -# include <zlib.h> /* odd include order-issue */ #else # include <unistd.h> /* FreeBSD, for write() and close(). */ #endif @@ -101,7 +101,12 @@ #include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_math_base.h" #include "BLI_mempool.h" +#include "BLI_threads.h" #include "MEM_guardedalloc.h" /* MEM_freeN */ #include "BKE_blender_version.h" @@ -129,14 +134,21 @@ #include <errno.h> +#include <zstd.h> + /* Make preferences read-only. */ #define U (*((const UserDef *)&U)) /* ********* my write, buffered writing with minimum size chunks ************ */ /* Use optimal allocation since blocks of this size are kept in memory for undo. */ -#define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ -#define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ +#define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */ +#define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */ + +#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */ +#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */ + +#define ZSTD_COMPRESSION_LEVEL 3 /** Use if we want to store how many bytes have been written to the file. */ // #define USE_WRITE_DATA_LEN @@ -147,9 +159,16 @@ typedef enum { WW_WRAP_NONE = 1, - WW_WRAP_ZLIB, + WW_WRAP_ZSTD, } eWriteWrapType; +typedef struct ZstdFrame { + struct ZstdFrame *next, *prev; + + uint32_t compressed_size; + uint32_t uncompressed_size; +} ZstdFrame; + typedef struct WriteWrap WriteWrap; struct WriteWrap { /* callbacks */ @@ -161,15 +180,23 @@ struct WriteWrap { bool use_buf; /* internal */ - union { - int file_handle; - gzFile gz_handle; - } _user_data; + int file_handle; + struct { + ListBase threadpool; + ListBase tasks; + ThreadMutex mutex; + ThreadCondition condition; + int next_frame; + int num_frames; + + int level; + ListBase frames; + + bool write_error; + } zstd; }; /* none */ -#define FILE_HANDLE(ww) (ww)->_user_data.file_handle - static bool ww_open_none(WriteWrap *ww, const char *filepath) { int file; @@ -177,7 +204,7 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666); if (file != -1) { - FILE_HANDLE(ww) = file; + ww->file_handle = file; return true; } @@ -185,39 +212,170 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath) } static bool ww_close_none(WriteWrap *ww) { - return (close(FILE_HANDLE(ww)) != -1); + return (close(ww->file_handle) != -1); } static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len) { - return write(FILE_HANDLE(ww), buf, buf_len); + return write(ww->file_handle, buf, buf_len); } -#undef FILE_HANDLE -/* zlib */ -#define FILE_HANDLE(ww) (ww)->_user_data.gz_handle +/* zstd */ -static bool ww_open_zlib(WriteWrap *ww, const char *filepath) +typedef struct { + struct ZstdWriteBlockTask *next, *prev; + void *data; + size_t size; + int frame_number; + WriteWrap *ww; +} ZstdWriteBlockTask; + +static void *zstd_write_task(void *userdata) { - gzFile file; + ZstdWriteBlockTask *task = userdata; + WriteWrap *ww = task->ww; - file = BLI_gzopen(filepath, "wb1"); + size_t out_buf_len = ZSTD_compressBound(task->size); + void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer"); + size_t out_size = ZSTD_compress( + out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL); - if (file != Z_NULL) { - FILE_HANDLE(ww) = file; - return true; + MEM_freeN(task->data); + + BLI_mutex_lock(&ww->zstd.mutex); + + while (ww->zstd.next_frame != task->frame_number) { + BLI_condition_wait(&ww->zstd.condition, &ww->zstd.mutex); } - return false; + if (ZSTD_isError(out_size)) { + ww->zstd.write_error = true; + } + else { + if (ww_write_none(ww, out_buf, out_size) == out_size) { + ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo"); + frameinfo->uncompressed_size = task->size; + frameinfo->compressed_size = out_size; + BLI_addtail(&ww->zstd.frames, frameinfo); + } + else { + ww->zstd.write_error = true; + } + } + + ww->zstd.next_frame++; + + BLI_mutex_unlock(&ww->zstd.mutex); + BLI_condition_notify_all(&ww->zstd.condition); + + MEM_freeN(out_buf); + return NULL; +} + +static bool ww_open_zstd(WriteWrap *ww, const char *filepath) +{ + if (!ww_open_none(ww, filepath)) { + return false; + } + + /* Leave one thread open for the main writing logic, unless we only have one HW thread. */ + int num_threads = max_ii(1, BLI_system_thread_count() - 1); + BLI_threadpool_init(&ww->zstd.threadpool, zstd_write_task, num_threads); + BLI_mutex_init(&ww->zstd.mutex); + BLI_condition_init(&ww->zstd.condition); + + return true; +} + +static void zstd_write_u32_le(WriteWrap *ww, uint32_t val) +{ +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(&val); +#endif + ww_write_none(ww, (char *)&val, sizeof(uint32_t)); } -static bool ww_close_zlib(WriteWrap *ww) + +/* In order to implement efficient seeking when reading the .blend, we append + * a skippable frame that encodes information about the other frames present + * in the file. + * The format here follows the upstream spec for seekable files: + * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md + * If this information is not present in a file (e.g. if it was compressed + * with external tools), it can still be opened in Blender, but seeking will + * not be supported, so more memory might be needed. */ +static void zstd_write_seekable_frames(WriteWrap *ww) { - return (gzclose(FILE_HANDLE(ww)) == Z_OK); + /* Write seek table header (magic number and frame size). */ + zstd_write_u32_le(ww, 0x184D2A5E); + + /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */ + const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames); + /* Each frame consists of two u32, so 8 bytes each. + * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */ + const uint32_t frame_size = num_frames * 8 + 9; + zstd_write_u32_le(ww, frame_size); + + /* Write seek table entries. */ + LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) { + zstd_write_u32_le(ww, frame->compressed_size); + zstd_write_u32_le(ww, frame->uncompressed_size); + } + + /* Write seek table footer (number of frames, option flags and second magic number). */ + zstd_write_u32_le(ww, num_frames); + const char flags = 0; /* We don't store checksums for each frame. */ + ww_write_none(ww, &flags, 1); + zstd_write_u32_le(ww, 0x8F92EAB1); } -static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len) + +static bool ww_close_zstd(WriteWrap *ww) { - return gzwrite(FILE_HANDLE(ww), buf, buf_len); + BLI_threadpool_end(&ww->zstd.threadpool); + BLI_freelistN(&ww->zstd.tasks); + + BLI_mutex_end(&ww->zstd.mutex); + BLI_condition_end(&ww->zstd.condition); + + zstd_write_seekable_frames(ww); + BLI_freelistN(&ww->zstd.frames); + + return ww_close_none(ww) && !ww->zstd.write_error; +} + +static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len) +{ + if (ww->zstd.write_error) { + return 0; + } + + ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__); + task->data = MEM_mallocN(buf_len, __func__); + memcpy(task->data, buf, buf_len); + task->size = buf_len; + task->frame_number = ww->zstd.num_frames++; + task->ww = ww; + + BLI_mutex_lock(&ww->zstd.mutex); + BLI_addtail(&ww->zstd.tasks, task); + + /* If there's a free worker thread, just push the block into that thread. + * Otherwise, we wait for the earliest thread to finish. + * We look up the earliest thread while holding the mutex, but release it + * before joining the thread to prevent a deadlock. */ + ZstdWriteBlockTask *first_task = ww->zstd.tasks.first; + BLI_mutex_unlock(&ww->zstd.mutex); + if (!BLI_available_threads(&ww->zstd.threadpool)) { + BLI_threadpool_remove(&ww->zstd.threadpool, first_task); + + /* If the task list was empty before we pushed our task, there should + * always be a free thread. */ + BLI_assert(first_task != task); + BLI_remlink(&ww->zstd.tasks, first_task); + MEM_freeN(first_task); + } + BLI_threadpool_insert(&ww->zstd.threadpool, task); + + return buf_len; } -#undef FILE_HANDLE /* --- end compression types --- */ @@ -226,11 +384,11 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) memset(r_ww, 0, sizeof(*r_ww)); switch (ww_type) { - case WW_WRAP_ZLIB: { - r_ww->open = ww_open_zlib; - r_ww->close = ww_close_zlib; - r_ww->write = ww_write_zlib; - r_ww->use_buf = false; + case WW_WRAP_ZSTD: { + r_ww->open = ww_open_zstd; + r_ww->close = ww_close_zstd; + r_ww->write = ww_write_zstd; + r_ww->use_buf = true; break; } default: { @@ -252,10 +410,17 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) typedef struct { const struct SDNA *sdna; - /** Use for file and memory writing (fixed size of #MYWRITE_BUFFER_SIZE). */ - uchar *buf; - /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ - size_t buf_used_len; + struct { + /** Use for file and memory writing (size stored in max_size). */ + uchar *buf; + /** Number of bytes used in #WriteData.buf (flushed when exceeded). */ + size_t used_len; + + /** Maximum size of the buffer. */ + size_t max_size; + /** Threshold above which writes get their own chunk. */ + size_t chunk_size; + } buffer; #ifdef USE_WRITE_DATA_LEN /** Total number of bytes written. */ @@ -271,7 +436,7 @@ typedef struct { bool use_memfile; /** - * Wrap writing, so we can use zlib or + * Wrap writing, so we can use zstd or * other compression types later, see: G_FILE_COMPRESS * Will be NULL for UNDO. */ @@ -291,7 +456,15 @@ static WriteData *writedata_new(WriteWrap *ww) wd->ww = ww; if ((ww == NULL) || (ww->use_buf)) { - wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf"); + if (ww == NULL) { + wd->buffer.max_size = MEM_BUFFER_SIZE; + wd->buffer.chunk_size = MEM_CHUNK_SIZE; + } + else { + wd->buffer.max_size = ZSTD_BUFFER_SIZE; + wd->buffer.chunk_size = ZSTD_CHUNK_SIZE; + } + wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf"); } return wd; @@ -325,8 +498,8 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen) static void writedata_free(WriteData *wd) { - if (wd->buf) { - MEM_freeN(wd->buf); + if (wd->buffer.buf) { + MEM_freeN(wd->buffer.buf); } MEM_freeN(wd); } @@ -343,9 +516,9 @@ static void writedata_free(WriteData *wd) */ static void mywrite_flush(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } } @@ -369,20 +542,20 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) wd->write_len += len; #endif - if (wd->buf == NULL) { + if (wd->buffer.buf == NULL) { writedata_do_write(wd, adr, len); } else { /* if we have a single big chunk, write existing data in * buffer and write out big chunk in smaller pieces */ - if (len > MYWRITE_MAX_CHUNK) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len > wd->buffer.chunk_size) { + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } do { - size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK); + size_t writelen = MIN2(len, wd->buffer.chunk_size); writedata_do_write(wd, adr, writelen); adr = (const char *)adr + writelen; len -= writelen; @@ -392,14 +565,14 @@ static void mywrite(WriteData *wd, const void *adr, size_t len) } /* if data would overflow buffer, write out the buffer */ - if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (len + wd->buffer.used_len > wd->buffer.max_size - 1) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } /* append data at end of buffer */ - memcpy(&wd->buf[wd->buf_used_len], adr, len); - wd->buf_used_len += len; + memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len); + wd->buffer.used_len += len; } } @@ -430,9 +603,9 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren */ static bool mywrite_end(WriteData *wd) { - if (wd->buf_used_len != 0) { - writedata_do_write(wd, wd->buf, wd->buf_used_len); - wd->buf_used_len = 0; + if (wd->buffer.used_len != 0) { + writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len); + wd->buffer.used_len = 0; } if (wd->use_memfile) { @@ -757,8 +930,8 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) BLO_write_struct(writer, bPathCompare, path_cmp); } - LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { - BLO_write_struct(writer, bUserAssetLibrary, asset_library); + LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library_ref, &userdef->asset_libraries) { + BLO_write_struct(writer, bUserAssetLibrary, asset_library_ref); } LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { @@ -982,6 +1155,14 @@ static bool write_file_handle(Main *mainvar, BLI_assert( (id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0); + /* We only write unused IDs in undo case. + * NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so + * their usercount should never be NULL currently. */ + if (id->us == 0 && !wd->use_memfile) { + BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS)); + continue; + } + const bool do_override = !ELEM(override_storage, NULL, bmain) && ID_IS_OVERRIDE_LIBRARY_REAL(id); @@ -1015,12 +1196,23 @@ static bool write_file_handle(Main *mainvar, memcpy(id_buffer, id, idtype_struct_size); + /* Clear runtime data to reduce false detection of changed data in undo/redo context. */ ((ID *)id_buffer)->tag = 0; + ((ID *)id_buffer)->us = 0; + ((ID *)id_buffer)->icon_id = 0; /* Those listbase data change every time we add/remove an ID, and also often when * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed' * detections between undo steps. */ ((ID *)id_buffer)->prev = NULL; ((ID *)id_buffer)->next = NULL; + /* Those runtime pointers should never be set during writing stage, but just in case clear + * them too. */ + ((ID *)id_buffer)->orig_id = NULL; + ((ID *)id_buffer)->newid = NULL; + /* Even though in theory we could be able to preserve this python instance across undo even + * when we need to re-read the ID into its original address, this is currently cleared in + * #direct_link_id_common in `readfile.c` anyway, */ + ((ID *)id_buffer)->py_instance = NULL; const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->blend_write != NULL) { @@ -1131,7 +1323,6 @@ bool BLO_write_file(Main *mainvar, ReportList *reports) { char tempname[FILE_MAX + 1]; - eWriteWrapType ww_type; WriteWrap ww; eBLO_WritePathRemap remap_mode = params->remap_mode; @@ -1153,14 +1344,7 @@ bool BLO_write_file(Main *mainvar, /* open temporary file, so we preserve the original in case we crash */ BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath); - if (write_flags & G_FILE_COMPRESS) { - ww_type = WW_WRAP_ZLIB; - } - else { - ww_type = WW_WRAP_NONE; - } - - ww_handle_init(ww_type, &ww); + ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww); if (ww.open(&ww, tempname) == false) { BKE_reportf( diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index 97a1a83792a..91e8a81aec0 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -74,10 +74,7 @@ static void free_locales(void) MEM_freeN((void *)locales); locales = NULL; } - if (locales_menu) { - MEM_freeN(locales_menu); - locales_menu = NULL; - } + MEM_SAFE_FREE(locales_menu); num_locales = num_locales_menu = 0; } diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 6dfaa0ca688..186c85abe58 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -85,10 +85,9 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter, dotprod = -dotprod; } const float fac = saacos(-dotprod); - /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */ - if (fac == fac) { - madd_v3_v3fl(v_no, f_no, fac); - } + /* Shouldn't happen as normalizing edge-vectors cause degenerate values to be zeroed out. */ + BLI_assert(!isnan(fac)); + madd_v3_v3fl(v_no, f_no, fac); } static void bm_vert_calc_normals_impl(BMVert *v) @@ -754,7 +753,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, /* Fix/update all clnors of this fan with computed average value. */ /* Prints continuously when merge custom normals, so commenting. */ - /* printf("Invalid clnors in this fan!\n"); */ + // printf("Invalid clnors in this fan!\n"); while ((clnor = BLI_SMALLSTACK_POP(clnors))) { // print_v2("org clnor", clnor); @@ -820,9 +819,10 @@ BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e, const BMLoop *l_a, const BMLoop *l_b) { + BLI_assert(l_a->radial_next == l_b); return ( /* The face is manifold. */ - (l_a->radial_next == l_b) && + (l_b->radial_next == l_a) && /* Faces have winding that faces the same way. */ (l_a->v != l_b->v) && /* The edge is smooth. */ @@ -931,6 +931,7 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm, const bool has_clnors = true; LinkNode *loops_of_vert = NULL; int loops_of_vert_count = 0; + /* When false the caller must have already tagged the edges. */ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); /* The loop with the lowest index. */ @@ -1039,6 +1040,7 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors( { const bool has_clnors = false; const short(*clnors_data)[2] = NULL; + /* When false the caller must have already tagged the edges. */ const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); const int cd_loop_clnors_offset = -1; @@ -1113,8 +1115,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, BMIter fiter; BMFace *f_curr; const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); - const bool check_angle = (split_angle < (float)M_PI); - const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; MLoopNorSpaceArray _lnors_spacearr = {NULL}; @@ -1138,10 +1138,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); } - if (split_angle_cos != -1.0f) { - bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); - } - /* Clear all loops' tags (means none are to be skipped for now). */ int index_face, index_loop = 0; BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) { @@ -1157,6 +1153,10 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); + /* Always tag edges based on winding & sharp edge flag + * (even when the auto-smooth angle doesn't need to be calculated). */ + bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); + /* We now know edges that can be smoothed (they are tagged), * and edges that will be hard (they aren't). * Now, time to generate the normals. @@ -1816,8 +1816,8 @@ void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all) BM_mesh_elem_index_ensure(bm, BM_VERT); /* When we affect a given vertex, we may affect following smooth fans: - * - all smooth fans of said vertex; - * - all smooth fans of all immediate loop-neighbors vertices; + * - all smooth fans of said vertex; + * - all smooth fans of all immediate loop-neighbors vertices; * This can be simplified as 'all loops of selected vertices and their immediate neighbors' * need to be tagged for update. */ diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index e66afcd88d9..7931e953295 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -203,9 +203,9 @@ static void *bmw_VertShellWalker_step(BMWalker *walker) curedge = shellWalk.curedge; do { if (!BLI_gset_haskey(walker->visit_set, curedge)) { - if (!walker->restrictflag || - (walker->restrictflag && - BMO_edge_flag_test(walker->bm, curedge, walker->restrictflag))) { + if (!walker->visibility_flag || + (walker->visibility_flag && + BMO_edge_flag_test(walker->bm, curedge, walker->visibility_flag))) { BMwShellWalker *newstate; v_old = BM_edge_other_vert(curedge, shellWalk.base); @@ -714,7 +714,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker) iwalk->base = owalk.base; #if 0 - if (!BMO_face_flag_test(walker->bm, l->f, walker->restrictflag)) { + if (!BMO_face_flag_test(walker->bm, l->f, walker->visibility_flag)) { iwalk->curloop = l->radial_next; } else { diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 8cc0bfadbda..57760900d45 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -327,7 +327,6 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) } #define VERT_KEEP 8 -#define VERT_IN 32 #define EDGE_MARK 1 diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c index b2b93bfd003..94856701e72 100644 --- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -37,14 +37,14 @@ struct BLaplacianSystem { float *eweights; /* Length weights per Edge. */ - float (*fweights)[3]; /* Cotangent weights per face. */ + float (*fweights)[3]; /* Cotangent weights per loop. */ float *ring_areas; /* Total area per ring. */ float *vlengths; /* Total sum of lengths(edges) per vertex. */ float *vweights; /* Total sum of weights per vertex. */ int numEdges; /* Number of edges. */ - int numFaces; /* Number of faces. */ + int numLoops; /* Number of loops. */ int numVerts; /* Number of verts. */ - short *zerola; /* Is zero area or length. */ + bool *zerola; /* Is zero area or length. */ /* Pointers to data. */ BMesh *bm; @@ -57,7 +57,7 @@ struct BLaplacianSystem { typedef struct BLaplacianSystem LaplacianSystem; static bool vert_is_boundary(BMVert *v); -static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts); +static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts); static void init_laplacian_matrix(LaplacianSystem *sys); static void delete_laplacian_system(LaplacianSystem *sys); static void delete_void_pointer(void *data); @@ -94,19 +94,19 @@ static void delete_laplacian_system(LaplacianSystem *sys) static void memset_laplacian_system(LaplacianSystem *sys, int val) { memset(sys->eweights, val, sizeof(float) * sys->numEdges); - memset(sys->fweights, val, sizeof(float) * sys->numFaces * 3); + memset(sys->fweights, val, sizeof(float[3]) * sys->numLoops); memset(sys->ring_areas, val, sizeof(float) * sys->numVerts); memset(sys->vlengths, val, sizeof(float) * sys->numVerts); memset(sys->vweights, val, sizeof(float) * sys->numVerts); - memset(sys->zerola, val, sizeof(short) * sys->numVerts); + memset(sys->zerola, val, sizeof(bool) * sys->numVerts); } -static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts) +static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts) { LaplacianSystem *sys; sys = MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem"); sys->numEdges = a_numEdges; - sys->numFaces = a_numFaces; + sys->numLoops = a_numLoops; sys->numVerts = a_numVerts; sys->eweights = MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight"); @@ -115,7 +115,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in return NULL; } - sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numFaces, "ModLaplSmoothFWeight"); + sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numLoops, "ModLaplSmoothFWeight"); if (!sys->fweights) { delete_laplacian_system(sys); return NULL; @@ -139,7 +139,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in return NULL; } - sys->zerola = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothZeloa"); + sys->zerola = MEM_callocN(sizeof(bool) * sys->numVerts, "ModLaplSmoothZeloa"); if (!sys->zerola) { delete_laplacian_system(sys); return NULL; @@ -166,219 +166,160 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in static void init_laplacian_matrix(LaplacianSystem *sys) { - float areaf; - float *v1, *v2, *v3, *v4; - float w1, w2, w3, w4; - int i, j; - bool has_4_vert; - uint idv1, idv2, idv3, idv4, idv[4]; BMEdge *e; BMFace *f; BMIter eiter; BMIter fiter; - BMIter vi; - BMVert *vn; - BMVert *vf[4]; - - BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, j) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { - v1 = e->v1->co; - v2 = e->v2->co; - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); - - w1 = len_v3v3(v1, v2); - if (w1 > sys->min_area) { - w1 = 1.0f / w1; - i = BM_elem_index_get(e); - sys->eweights[i] = w1; - sys->vlengths[idv1] += w1; - sys->vlengths[idv2] += w1; - } - else { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; - } - } - } - - BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + uint i; - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { - vf[i] = vn; - } - has_4_vert = (i == 4) ? 1 : 0; - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0; - - v1 = vf[0]->co; - v2 = vf[1]->co; - v3 = vf[2]->co; - v4 = has_4_vert ? vf[3]->co : NULL; - - if (has_4_vert) { - areaf = area_quad_v3(v1, v2, v3, v4); - } - else { - areaf = area_tri_v3(v1, v2, v3); - } + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { + continue; + } - if (fabsf(areaf) < sys->min_area) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; - sys->zerola[idv3] = 1; - if (has_4_vert) { - sys->zerola[idv4] = 1; - } - } + const float *v1 = e->v1->co; + const float *v2 = e->v2->co; + const int idv1 = BM_elem_index_get(e->v1); + const int idv2 = BM_elem_index_get(e->v2); + + float w1 = len_v3v3(v1, v2); + if (w1 > sys->min_area) { + w1 = 1.0f / w1; + sys->eweights[i] = w1; + sys->vlengths[idv1] += w1; + sys->vlengths[idv2] += w1; + } + else { + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; + } + } - sys->ring_areas[idv1] += areaf; - sys->ring_areas[idv2] += areaf; - sys->ring_areas[idv3] += areaf; - if (has_4_vert) { - sys->ring_areas[idv4] += areaf; - } + uint l_curr_index = 0; - if (has_4_vert) { + BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + l_curr_index += f->len; + continue; + } - idv[0] = idv1; - idv[1] = idv2; - idv[2] = idv3; - idv[3] = idv4; + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; + l_iter = l_first; + do { + const int vi_prev = BM_elem_index_get(l_iter->prev->v); + const int vi_curr = BM_elem_index_get(l_iter->v); + const int vi_next = BM_elem_index_get(l_iter->next->v); - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; + const float *co_prev = l_iter->prev->v->co; + const float *co_curr = l_iter->v->co; + const float *co_next = l_iter->next->v->co; - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); + const float areaf = area_tri_v3(co_prev, co_curr, co_next); - sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; - } + if (areaf < sys->min_area) { + sys->zerola[vi_curr] = true; } - else { - i = BM_elem_index_get(f); - w1 = cotangent_tri_weight_v3(v1, v2, v3); - w2 = cotangent_tri_weight_v3(v2, v3, v1); - w3 = cotangent_tri_weight_v3(v3, v1, v2); + sys->ring_areas[vi_prev] += areaf; + sys->ring_areas[vi_curr] += areaf; + sys->ring_areas[vi_next] += areaf; - sys->fweights[i][0] += w1; - sys->fweights[i][1] += w2; - sys->fweights[i][2] += w3; + const float w1 = cotangent_tri_weight_v3(co_curr, co_next, co_prev) / 2.0f; + const float w2 = cotangent_tri_weight_v3(co_next, co_prev, co_curr) / 2.0f; + const float w3 = cotangent_tri_weight_v3(co_prev, co_curr, co_next) / 2.0f; - sys->vweights[idv1] += w2 + w3; - sys->vweights[idv2] += w1 + w3; - sys->vweights[idv3] += w1 + w2; - } - } + sys->fweights[l_curr_index][0] += w1; + sys->fweights[l_curr_index][1] += w2; + sys->fweights[l_curr_index][2] += w3; + + sys->vweights[vi_prev] += w1 + w2; + sys->vweights[vi_curr] += w2 + w3; + sys->vweights[vi_next] += w1 + w3; + } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first)); } } static void fill_laplacian_matrix(LaplacianSystem *sys) { - float *v1, *v2, *v3, *v4; - float w2, w3, w4; - int i, j; - bool has_4_vert; - uint idv1, idv2, idv3, idv4, idv[4]; - BMEdge *e; BMFace *f; BMIter eiter; BMIter fiter; - BMIter vi; - BMVert *vn; - BMVert *vf[4]; + int i; + + uint l_curr_index = 0; BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { - vf[i] = vn; + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + l_curr_index += f->len; + continue; + } + + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + + int vi_prev = BM_elem_index_get(l_iter->prev->v); + int vi_curr = BM_elem_index_get(l_iter->v); + + bool ok_prev = (sys->zerola[vi_prev] == false) && !vert_is_boundary(l_iter->prev->v); + bool ok_curr = (sys->zerola[vi_curr] == false) && !vert_is_boundary(l_iter->v); + + do { + const int vi_next = BM_elem_index_get(l_iter->next->v); + const bool ok_next = (sys->zerola[vi_next] == false) && !vert_is_boundary(l_iter->next->v); + + if (ok_prev) { + EIG_linear_solver_matrix_add(sys->context, + vi_prev, + vi_curr, + sys->fweights[l_curr_index][1] * sys->vweights[vi_prev]); + EIG_linear_solver_matrix_add(sys->context, + vi_prev, + vi_next, + sys->fweights[l_curr_index][0] * sys->vweights[vi_prev]); } - has_4_vert = (i == 4) ? 1 : 0; - if (has_4_vert) { - idv[0] = BM_elem_index_get(vf[0]); - idv[1] = BM_elem_index_get(vf[1]); - idv[2] = BM_elem_index_get(vf[2]); - idv[3] = BM_elem_index_get(vf[3]); - for (j = 0; j < 4; j++) { - idv1 = idv[j]; - idv2 = idv[(j + 1) % 4]; - idv3 = idv[(j + 2) % 4]; - idv4 = idv[(j + 3) % 4]; - - v1 = vf[j]->co; - v2 = vf[(j + 1) % 4]->co; - v3 = vf[(j + 2) % 4]->co; - v4 = vf[(j + 3) % 4]->co; - - w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2); - w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3); - w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1); - - w2 = w2 / 4.0f; - w3 = w3 / 4.0f; - w4 = w4 / 4.0f; - - if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == 0) { - EIG_linear_solver_matrix_add(sys->context, idv1, idv2, w2 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv3, w3 * sys->vweights[idv1]); - EIG_linear_solver_matrix_add(sys->context, idv1, idv4, w4 * sys->vweights[idv1]); - } - } + if (ok_curr) { + EIG_linear_solver_matrix_add(sys->context, + vi_curr, + vi_next, + sys->fweights[l_curr_index][2] * sys->vweights[vi_curr]); + EIG_linear_solver_matrix_add(sys->context, + vi_curr, + vi_prev, + sys->fweights[l_curr_index][1] * sys->vweights[vi_curr]); } - else { - idv1 = BM_elem_index_get(vf[0]); - idv2 = BM_elem_index_get(vf[1]); - idv3 = BM_elem_index_get(vf[2]); - /* Is ring if number of faces == number of edges around vertice. */ - i = BM_elem_index_get(f); - if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); - EIG_linear_solver_matrix_add( - sys->context, idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); - } - if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); - EIG_linear_solver_matrix_add( - sys->context, idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); - } - if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == 0) { - EIG_linear_solver_matrix_add( - sys->context, idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); - EIG_linear_solver_matrix_add( - sys->context, idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); - } + if (ok_next) { + EIG_linear_solver_matrix_add(sys->context, + vi_next, + vi_curr, + sys->fweights[l_curr_index][2] * sys->vweights[vi_next]); + EIG_linear_solver_matrix_add(sys->context, + vi_next, + vi_prev, + sys->fweights[l_curr_index][0] * sys->vweights[vi_next]); } - } + + vi_prev = vi_curr; + vi_curr = vi_next; + + ok_prev = ok_curr; + ok_curr = ok_next; + + } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first)); } - BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { - v1 = e->v1->co; - v2 = e->v2->co; - idv1 = BM_elem_index_get(e->v1); - idv2 = BM_elem_index_get(e->v2); - if (sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { - i = BM_elem_index_get(e); - EIG_linear_solver_matrix_add( - sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); - EIG_linear_solver_matrix_add( - sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); - } + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) { + continue; + } + const uint idv1 = BM_elem_index_get(e->v1); + const uint idv2 = BM_elem_index_get(e->v2); + if (sys->zerola[idv1] == false && sys->zerola[idv2] == false) { + EIG_linear_solver_matrix_add( + sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); + EIG_linear_solver_matrix_add( + sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); } } } @@ -453,8 +394,8 @@ static void validate_solution( lene = len_v3v3(ve1, ve2); if (lene > leni * SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE || lene < leni * SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; } } @@ -463,7 +404,7 @@ static void validate_solution( } BMO_ITER (v, &siter, sys->op->slots_in, "verts", BM_VERT) { m_vertex_id = BM_elem_index_get(v); - if (sys->zerola[m_vertex_id] == 0) { + if (sys->zerola[m_vertex_id] == false) { if (usex) { v->co[0] = EIG_linear_solver_variable_get(sys->context, 0, m_vertex_id); } @@ -495,7 +436,7 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) if (bm->totface == 0) { return; } - sys = init_laplacian_system(bm->totedge, bm->totface, bm->totvert); + sys = init_laplacian_system(bm->totedge, bm->totloop, bm->totvert); if (!sys) { return; } @@ -533,7 +474,10 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) EIG_linear_solver_right_hand_side_add(sys->context, 1, m_vertex_id, v->co[1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, m_vertex_id, v->co[2]); i = m_vertex_id; - if (sys->zerola[i] == 0) { + if ((sys->zerola[i] == false) && + /* Non zero check is to account for vertices that aren't connected to a selected face. + * Without this wire edges become `nan`, see T89214. */ + (sys->ring_areas[i] != 0.0f)) { w = sys->vweights[i] * sys->ring_areas[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -lambda_factor / (4.0f * w); w = sys->vlengths[i]; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 0f99f04ad57..e306fe47770 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -2511,7 +2511,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) pnorm = lnext->f->no; } else { - /* printf("unexpected harden case (edge)\n"); */ + // printf("unexpected harden case (edge)\n"); } } else if (fkind == F_VERT) { @@ -2554,7 +2554,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm) pnorm = norm; } else { - /* printf("unexpected harden case (vert)\n"); */ + // printf("unexpected harden case (vert)\n"); } } } diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 20b6903b239..97fccbe01fd 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -935,9 +935,9 @@ static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first) } /** - * special, highly limited edge collapse function + * Special, highly limited edge collapse function * intended for speed over flexibility. - * can only collapse edges connected to (1, 2) tris. + * can only collapse edges connected to (1, 2) triangles. * * Important - don't add vert/edge/face data on collapsing! * diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 000ba298c2d..ee287c65fe9 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -597,16 +597,16 @@ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} - DEPENDS smaa_areatex + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} + DEPENDS smaa_areatex ) add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} + SOURCES ${GENSRC} ) list(APPEND SRC - ${GENSRC} + ${GENSRC} ) unset(GENSRC) unset(GENSRC_DIR) @@ -650,4 +650,3 @@ if(WITH_GTESTS) include(GTestTesting) blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") endif() - diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 900f29db44c..40a1e0da2a8 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -62,12 +62,18 @@ constexpr int COM_data_type_num_channels(const DataType datatype) } } +constexpr int COM_data_type_bytes_len(DataType data_type) +{ + return COM_data_type_num_channels(data_type) * sizeof(float); +} + constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value); constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f}; constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f}; +constexpr float COM_COLOR_BLACK[4] = {0.0f, 0.0f, 0.0f, 1.0f}; constexpr float COM_VALUE_ZERO[1] = {0.0f}; constexpr float COM_VALUE_ONE[1] = {1.0f}; diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc index 90c97f2a9c7..cafdff89c8e 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.cc +++ b/source/blender/compositor/intern/COM_BufferOperation.cc @@ -32,6 +32,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type) setResolution(resolution); addOutputSocket(data_type); flags.is_constant_operation = buffer_->is_a_single_elem(); + flags.is_fullframe_operation = false; } const float *BufferOperation::get_constant_elem() @@ -40,20 +41,32 @@ const float *BufferOperation::get_constant_elem() return buffer_->getBuffer(); } +void BufferOperation::initExecution() +{ + if (buffer_->is_a_single_elem()) { + initMutex(); + } +} + void *BufferOperation::initializeTileData(rcti * /*rect*/) { if (buffer_->is_a_single_elem() == false) { return buffer_; } + lockMutex(); if (!inflated_buffer_) { inflated_buffer_ = buffer_->inflate(); } + unlockMutex(); return inflated_buffer_; } void BufferOperation::deinitExecution() { + if (buffer_->is_a_single_elem()) { + deinitMutex(); + } delete inflated_buffer_; } diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h index 705264c37b7..b4cbc0a56b6 100644 --- a/source/blender/compositor/intern/COM_BufferOperation.h +++ b/source/blender/compositor/intern/COM_BufferOperation.h @@ -32,6 +32,7 @@ class BufferOperation : public ConstantOperation { const float *get_constant_elem() override; void *initializeTileData(rcti *rect) override; + void initExecution() override; void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index 5b48ff8fc08..445a9ce7433 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -44,7 +44,9 @@ static bool is_constant_foldable(NodeOperation *operation) { if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) { for (int i = 0; i < operation->getNumberOfInputSockets(); i++) { - if (!operation->get_input_operation(i)->get_flags().is_constant_operation) { + NodeOperation *input = operation->get_input_operation(i); + if (!input->get_flags().is_constant_operation || + !static_cast<ConstantOperation *>(input)->can_get_constant_elem()) { return false; } } diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index 5443974cbb0..a0333cf96cf 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -178,21 +178,27 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system, } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket); switch (socket->getDataType()) { - case DataType::Value: - if (typeid(*operation) == typeid(SetValueOperation)) { - const float value = ((SetValueOperation *)operation)->getValue(); + case DataType::Value: { + ConstantOperation *constant = operation->get_flags().is_constant_operation ? + static_cast<ConstantOperation *>(operation) : + nullptr; + if (constant && constant->can_get_constant_elem()) { + const float value = *constant->get_constant_elem(); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value\\n%12.4g", value); } else { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value"); } break; - case DataType::Vector: + } + case DataType::Vector: { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector"); break; - case DataType::Color: + } + case DataType::Color: { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color"); break; + } } } len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}"); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index ae12c444dc1..310e87b6a4b 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -260,6 +260,11 @@ class MemoryBuffer { return this->m_num_channels; } + uint8_t get_elem_bytes_len() const + { + return this->m_num_channels * sizeof(float); + } + /** * Get all buffer elements as a range with no offsets. */ diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc index d025ce53330..a081b80349d 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -45,7 +45,7 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context, group->determineResolution(resolution); if (border_.use_render_border) { - const rctf *render_border = border_.viewer_border; + const rctf *render_border = border_.render_border; group->setRenderBorder( render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); } diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index ef171c01653..b5b23798160 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -41,7 +41,7 @@ void MaskNode::convertToOperations(NodeConverter &converter, NodeMask *data = (NodeMask *)editorNode->storage; Mask *mask = (Mask *)editorNode->id; - // always connect the output image + /* Always connect the output image. */ MaskOperation *operation = new MaskOperation(); if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) { diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index 50bd9b4d71b..b80071d27c7 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -62,7 +62,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, } } - // always connect the output image + /* Always connect the output image. */ MovieClipOperation *operation = new MovieClipOperation(); operation->setMovieClip(movieClip); operation->setMovieClipUser(movieClipUser); diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 0c656753a51..30e7fab4027 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverKeyOperation::AlphaOverKeyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverKeyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,29 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4], } } +void AlphaOverKeyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float premul = value * over_color[3]; + const float mul = 1.0f - premul; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h index 83713d18971..960fbc98fe9 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverKeyOperation : public MixBaseOperation { public: + AlphaOverKeyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index c68c79d2263..0cc179ea209 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -23,6 +23,7 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { this->m_x = 0.0f; + this->flags.can_be_constant = true; } void AlphaOverMixedOperation::executePixelSampled(float output[4], @@ -56,4 +57,30 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4], } } +void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x; + const float premul = value * addfac; + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + premul * over_color[0]; + p.out[1] = (mul * color1[1]) + premul * over_color[1]; + p.out[2] = (mul * color1[2]) + premul * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index e2b3af84162..2b88cd5f421 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -45,6 +45,8 @@ class AlphaOverMixedOperation : public MixBaseOperation { { this->m_x = x; } + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index 3dd4607e273..a57e8c7f8a3 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -20,6 +20,11 @@ namespace blender::compositor { +AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() +{ + this->flags.can_be_constant = true; +} + void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], float x, float y, @@ -50,4 +55,28 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], } } +void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p) +{ + for (; p.out < p.row_end; p.next()) { + const float *color1 = p.color1; + const float *over_color = p.color2; + const float value = *p.value; + + if (over_color[3] <= 0.0f) { + copy_v4_v4(p.out, color1); + } + else if (value == 1.0f && over_color[3] >= 1.0f) { + copy_v4_v4(p.out, over_color); + } + else { + const float mul = 1.0f - value * over_color[3]; + + p.out[0] = (mul * color1[0]) + value * over_color[0]; + p.out[1] = (mul * color1[1]) + value * over_color[1]; + p.out[2] = (mul * color1[2]) + value * over_color[2]; + p.out[3] = (mul * color1[3]) + value * over_color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h index f1d4b668fce..701bc07cc27 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h @@ -28,10 +28,14 @@ namespace blender::compositor { */ class AlphaOverPremultiplyOperation : public MixBaseOperation { public: + AlphaOverPremultiplyOperation(); + /** * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 64448e2ae95..0c1bb688d4e 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -38,7 +38,6 @@ void BilateralBlurOperation::initExecution() { this->m_inputColorProgram = getInputSocketReader(0); this->m_inputDeterminatorProgram = getInputSocketReader(1); - this->m_space = this->m_data->sigma_space + this->m_data->iter; QualityStepHelper::initExecution(COM_QH_INCREASE); } @@ -115,4 +114,89 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + const int add = ceil(this->m_space) + 1; + + r_input_area.xmax = output_area.xmax + (add); + r_input_area.xmin = output_area.xmin - (add); + r_input_area.ymax = output_area.ymax + (add); + r_input_area.ymin = output_area.ymin - (add); +} + +struct PixelCursor { + MemoryBuffer *input_determinator; + MemoryBuffer *input_color; + int step; + float sigma_color; + const float *determ_reference_color; + float temp_color[4]; + float *out; + int min_x, max_x; + int min_y, max_y; +}; + +static void blur_pixel(PixelCursor &p) +{ + float blur_divider = 0.0f; + zero_v4(p.out); + + /* TODO(sergey): This isn't really good bilateral filter, it should be + * using gaussian bell for weights. Also sigma_color doesn't seem to be + * used correct at all. + */ + for (int yi = p.min_y; yi < p.max_y; yi += p.step) { + for (int xi = p.min_x; xi < p.max_x; xi += p.step) { + p.input_determinator->read(p.temp_color, xi, yi); + /* Do not take the alpha channel into account. */ + const float delta_color = (fabsf(p.determ_reference_color[0] - p.temp_color[0]) + + fabsf(p.determ_reference_color[1] - p.temp_color[1]) + + fabsf(p.determ_reference_color[2] - p.temp_color[2])); + if (delta_color < p.sigma_color) { + /* Add this to the blur. */ + p.input_color->read(p.temp_color, xi, yi); + add_v4_v4(p.out, p.temp_color); + blur_divider += 1.0f; + } + } + } + + if (blur_divider > 0.0f) { + mul_v4_fl(p.out, 1.0f / blur_divider); + } + else { + copy_v4_v4(p.out, COM_COLOR_BLACK); + } +} + +void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + PixelCursor p = {}; + p.step = QualityStepHelper::getStep(); + p.sigma_color = this->m_data->sigma_color; + p.input_color = inputs[0]; + p.input_determinator = inputs[1]; + const float space = this->m_space; + for (int y = area.ymin; y < area.ymax; y++) { + p.out = output->get_elem(area.xmin, y); + /* This will be used as the reference color for the determinator. */ + p.determ_reference_color = p.input_determinator->get_elem(area.xmin, y); + p.min_y = floor(y - space); + p.max_y = ceil(y + space); + for (int x = area.xmin; x < area.xmax; x++) { + p.min_x = floor(x - space); + p.max_x = ceil(x + space); + + blur_pixel(p); + + p.determ_reference_color += p.input_determinator->elem_stride; + p.out += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index c56cef35050..517c5292827 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { +class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputColorProgram; SocketReader *m_inputDeterminatorProgram; @@ -55,7 +55,14 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper { void setData(NodeBilateralBlurData *data) { this->m_data = data; + this->m_space = data->sigma_space + data->iter; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index 63f283b6acc..bd5b25b5af8 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -110,6 +110,31 @@ void BokehImageOperation::executePixelSampled(float output[4], output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; } +void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + const float shift = this->m_data->lensshift; + const float shift2 = shift / 2.0f; + const float distance = this->m_circularDistance; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); + const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); + const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y); + if (shift < 0) { + it.out[0] = insideBokehMax; + it.out[1] = insideBokehMed; + it.out[2] = insideBokehMin; + } + else { + it.out[0] = insideBokehMin; + it.out[1] = insideBokehMed; + it.out[2] = insideBokehMax; + } + it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + } +} + void BokehImageOperation::deinitExecution() { if (this->m_deleteData) { diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 2e0bc8a34dc..2527233fabd 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -49,7 +49,7 @@ namespace blender::compositor { * With a simple compare it can be detected if the evaluated pixel is between the outer and inner *edge. */ -class BokehImageOperation : public NodeOperation { +class BokehImageOperation : public MultiThreadedOperation { private: /** * \brief Settings of the bokeh image @@ -151,6 +151,10 @@ class BokehImageOperation : public NodeOperation { { this->m_deleteData = true; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index eee007ce9e6..1e3e7806968 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -28,6 +28,7 @@ ChangeHSVOperation::ChangeHSVOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Color); this->m_inputOperation = nullptr; + this->flags.can_be_constant = true; } void ChangeHSVOperation::initExecution() @@ -71,4 +72,26 @@ void ChangeHSVOperation::executePixelSampled(float output[4], output[3] = inputColor1[3]; } +void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + const float hue = *it.in(1); + it.out[0] = color[0] + (hue - 0.5f); + if (it.out[0] > 1.0f) { + it.out[0] -= 1.0f; + } + else if (it.out[0] < 0.0f) { + it.out[0] += 1.0f; + } + const float saturation = *it.in(2); + const float value = *it.in(3); + it.out[1] = color[1] * saturation; + it.out[2] = color[2] * value; + it.out[3] = color[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index d38b4be3efe..e7bc3274f25 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_MixOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ChangeHSVOperation : public NodeOperation { +class ChangeHSVOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; SocketReader *m_hueOperation; @@ -46,6 +46,10 @@ class ChangeHSVOperation : public NodeOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index cb0565a81a2..1b7ad0ea608 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -98,6 +98,36 @@ void ColorCurveOperation::deinitExecution() this->m_inputWhiteProgram = nullptr; } +void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + float bwmul[3]; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + /* Local versions of `cumap->black` and `cumap->white`. */ + const float *black = it.in(2); + const float *white = it.in(3); + /* Get a local `bwmul` value, it's not threadsafe using `cumap->bwmul` and others. */ + BKE_curvemapping_set_black_white_ex(black, white, bwmul); + + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, it.out, image, black, bwmul); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF_ex(cumap, col, image, black, bwmul); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + // Constant level curve mapping ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() @@ -154,4 +184,27 @@ void ConstantLevelColorCurveOperation::deinitExecution() this->m_inputImageProgram = nullptr; } +void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *cumap = this->m_curveMapping; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float fac = *it.in(0); + const float *image = it.in(1); + if (fac >= 1.0f) { + BKE_curvemapping_evaluate_premulRGBF(cumap, it.out, image); + } + else if (fac <= 0.0f) { + copy_v3_v3(it.out, image); + } + else { + float col[4]; + BKE_curvemapping_evaluate_premulRGBF(cumap, col, image); + interp_v3_v3v3(it.out, image, col, fac); + } + it.out[3] = image[3]; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index 6fc7759b8d2..d8271e56d1d 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -51,6 +51,10 @@ class ColorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ConstantLevelColorCurveOperation : public CurveBaseOperation { @@ -89,6 +93,10 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { { copy_v3_v3(this->m_white, white); } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index 94d41b28f5d..8752d764107 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -220,6 +220,22 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) } } +void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (!m_outputBuffer) { + return; + } + MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + output_buf.copy_from(inputs[0], area); + if (this->m_useAlphaInput) { + output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + } + MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + depth_buf.copy_from(inputs[2], area); +} + void CompositorOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 65988c86cc5..66367ec8bae 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -20,7 +20,7 @@ #include "BLI_rect.h" #include "BLI_string.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" struct Scene; @@ -29,7 +29,7 @@ namespace blender::compositor { /** * \brief Compositor output operation */ -class CompositorOperation : public NodeOperation { +class CompositorOperation : public MultiThreadedOperation { private: const struct Scene *m_scene; /** @@ -125,6 +125,10 @@ class CompositorOperation : public NodeOperation { { this->m_active = active; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index f905edbde76..33d51cca432 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -22,7 +22,24 @@ namespace blender::compositor { ConstantOperation::ConstantOperation() { + needs_resolution_to_get_constant_ = false; flags.is_constant_operation = true; + flags.is_fullframe_operation = true; +} + +bool ConstantOperation::can_get_constant_elem() const +{ + return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set; +} + +void ConstantOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + BLI_assert(output->is_a_single_elem()); + const float *constant = get_constant_elem(); + float *out = output->get_elem(area.xmin, area.ymin); + memcpy(out, constant, output->get_elem_bytes_len()); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index 2709efeebd8..31b8d30254b 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -22,15 +22,27 @@ namespace blender::compositor { +/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution + * for all constant operations and make all initialization and deinitilization methods final. */ /** - * Base class for primitive constant operations (Color/Vector/Value). The rest of operations that - * can be constant are evaluated into primitives during constant folding. + * Base class for operations that are always constant. Operations that can be constant only when + * all their inputs are so, are evaluated into primitive constants (Color/Vector/Value) during + * constant folding. */ class ConstantOperation : public NodeOperation { + protected: + bool needs_resolution_to_get_constant_; + public: ConstantOperation(); + /** May require resolution to be already determined. */ virtual const float *get_constant_elem() = 0; + bool can_get_constant_elem() const; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 52ae1d6d5b5..1a86fadad76 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -57,8 +57,8 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat ::memcpy(&m3hash, &input[0], sizeof(uint32_t)); /* Since the red channel is likely to be out of display range, * setting green and blue gives more meaningful images. */ - output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX); - output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX); + output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); + output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } for (float hash : m_objectIndex) { if (input[0] == hash) { diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 8f655964570..3c4b27aa4cf 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -25,6 +25,7 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { this->m_curveMapping = nullptr; + this->flags.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index fff0f3168ba..da665e7ea60 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_color_types.h" namespace blender::compositor { -class CurveBaseOperation : public NodeOperation { +class CurveBaseOperation : public MultiThreadedOperation { protected: /** * Cached reference to the inputProgram diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 2454f507664..a27148f967d 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -24,7 +24,7 @@ namespace blender::compositor { -// DilateErode Distance Threshold +/* DilateErode Distance Threshold */ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { this->addInputSocket(DataType::Value); @@ -258,7 +258,7 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); } -// Erode Distance +/* Erode Distance */ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() { /* pass */ @@ -318,7 +318,7 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); } -// Dilate step +/* Dilate step */ DilateStepOperation::DilateStepOperation() { this->addInputSocket(DataType::Value); @@ -331,7 +331,7 @@ void DilateStepOperation::initExecution() this->m_inputProgram = this->getInputSocketReader(0); } -// small helper to pass data from initializeTileData to executePixel +/* Small helper to pass data from initializeTileData to executePixel. */ struct tile_info { rcti rect; int width; @@ -370,21 +370,21 @@ void *DilateStepOperation::initializeTileData(rcti *rect) int bwidth = rect->xmax - rect->xmin; int bheight = rect->ymax - rect->ymin; - // NOTE: Cache buffer has original tilesize width, but new height. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tile-size width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode. */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = -FLT_MAX; @@ -409,7 +409,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = -FLT_MAX; @@ -475,7 +475,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -// Erode step +/* Erode step */ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() { /* pass */ @@ -500,21 +500,21 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) int bwidth = rect->xmax - rect->xmin; int bheight = rect->ymax - rect->ymin; - // NOTE: Cache buffer has original tilesize width, but new height. - // We have to calculate the additional rows in the first pass, - // to have valid data available for the second pass. + /* NOTE: Cache buffer has original tile-size width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax); float *rectf = result->buffer; - // temp holds maxima for every step in the algorithm, buf holds a - // single row or column of input values, padded with FLT_MAX's to - // simplify the logic. + /* temp holds maxima for every step in the algorithm, buf holds a + * single row or column of input values, padded with FLT_MAX's to + * simplify the logic. */ float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), "dilate erode buf"); - // The following is based on the van Herk/Gil-Werman algorithm for morphology operations. - // first pass, horizontal dilate/erode + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. + * first pass, horizontal dilate/erode */ for (y = ymin; y < ymax; y++) { for (x = 0; x < bwidth + 5 * half_window; x++) { buf[x] = FLT_MAX; @@ -539,7 +539,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) } } - // second pass, vertical dilate/erode + /* Second pass, vertical dilate/erode. */ for (x = 0; x < bwidth; x++) { for (y = 0; y < bheight + 5 * half_window; y++) { buf[y] = FLT_MAX; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 50db4e02940..6dac6f5ecc7 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -23,8 +23,8 @@ namespace blender::compositor { -/* utility functions used by glare, tonemap and lens distortion */ -/* soms macros for color handling */ +/* Utility functions used by glare, tone-map and lens distortion. */ +/* Some macros for color handling. */ typedef float fRGB[4]; /* TODO: replace with BLI_math_vector. */ diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index e341a88ff71..5ae868c5964 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -73,4 +73,31 @@ void HueSaturationValueCorrectOperation::deinitExecution() this->m_inputProgram = nullptr; } +void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + float hsv[4]; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(hsv, it.in(0)); + + /* Adjust hue, scaling returned default 0.5 up to 1. */ + float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + hsv[0] += f - 0.5f; + + /* Adjust saturation, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + hsv[1] *= (f * 2.0f); + + /* Adjust value, scaling returned default 0.5 up to 1. */ + f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + hsv[2] *= (f * 2.0f); + + hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ + CLAMP(hsv[1], 0.0f, 1.0f); + + copy_v4_v4(it.out, hsv); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 703b2894bdb..6c1b66aba1f 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -47,6 +47,10 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index 339e40a5d1f..4f71a1d0d1d 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -30,6 +30,7 @@ InvertOperation::InvertOperation() this->m_color = true; this->m_alpha = false; setResolutionInputSocketIndex(1); + this->flags.can_be_constant = true; } void InvertOperation::initExecution() { @@ -70,4 +71,31 @@ void InvertOperation::deinitExecution() this->m_inputColorProgram = nullptr; } +void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float value = *it.in(0); + const float inverted_value = 1.0f - value; + const float *color = it.in(1); + + if (this->m_color) { + it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; + it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; + it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; + } + else { + copy_v3_v3(it.out, color); + } + + if (this->m_alpha) { + it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; + } + else { + it.out[3] = color[3]; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index 17e5eb95f3e..a084bf5d559 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class InvertOperation : public NodeOperation { +class InvertOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -59,6 +59,10 @@ class InvertOperation : public NodeOperation { { this->m_alpha = alpha; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index c7763f08e71..84992f23924 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -161,4 +161,41 @@ void MaskOperation::executePixelSampled(float output[4], } } +void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + Vector<MaskRasterHandle *> handles = get_non_null_handles(); + if (handles.size() == 0) { + output->fill(area, COM_VALUE_ZERO); + return; + } + + float xy[2]; + for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { + xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0]; + xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1]; + *it.out = 0.0f; + for (MaskRasterHandle *handle : handles) { + *it.out += BKE_maskrasterize_handle_sample(handle, xy); + } + + /* Until we get better falloff. */ + *it.out /= m_rasterMaskHandleTot; + } +} + +Vector<MaskRasterHandle *> MaskOperation::get_non_null_handles() const +{ + Vector<MaskRasterHandle *> handles; + for (int i = 0; i < m_rasterMaskHandleTot; i++) { + MaskRasterHandle *handle = m_rasterMaskHandles[i]; + if (handle == nullptr) { + continue; + } + handles.append(handle); + } + return handles; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index e8cd9c722df..81e344c0451 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -19,7 +19,7 @@ #pragma once #include "BLI_listbase.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_mask_types.h" #include "IMB_imbuf_types.h" @@ -31,7 +31,7 @@ namespace blender::compositor { /** * Class with implementation of mask rasterization */ -class MaskOperation : public NodeOperation { +class MaskOperation : public MultiThreadedOperation { protected: Mask *m_mask; @@ -98,6 +98,13 @@ class MaskOperation : public NodeOperation { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + Vector<MaskRasterHandle *> get_non_null_handles() const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index a9f187258b2..e36e93984fb 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -29,10 +29,21 @@ MovieClipAttributeOperation::MovieClipAttributeOperation() this->m_framenumber = 0; this->m_attribute = MCA_X; this->m_invert = false; + needs_resolution_to_get_constant_ = true; + is_value_calculated_ = false; } void MovieClipAttributeOperation::initExecution() { + if (!is_value_calculated_) { + calc_value(); + } +} + +void MovieClipAttributeOperation::calc_value() +{ + BLI_assert(this->get_flags().is_resolution_set); + is_value_calculated_ = true; if (this->m_clip == nullptr) { return; } @@ -83,4 +94,12 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2] resolution[1] = preferredResolution[1]; } +const float *MovieClipAttributeOperation::get_constant_elem() +{ + if (!is_value_calculated_) { + calc_value(); + } + return &m_value; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 8507e98d08f..28c39d4dad3 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -18,6 +18,7 @@ #pragma once +#include "COM_ConstantOperation.h" #include "COM_NodeOperation.h" #include "DNA_movieclip_types.h" @@ -33,13 +34,14 @@ typedef enum MovieClipAttribute { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MovieClipAttributeOperation : public NodeOperation { +class MovieClipAttributeOperation : public ConstantOperation { private: MovieClip *m_clip; float m_value; int m_framenumber; bool m_invert; MovieClipAttribute m_attribute; + bool is_value_calculated_; public: /** @@ -56,6 +58,8 @@ class MovieClipAttributeOperation : public NodeOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; + const float *get_constant_elem() override; + void setMovieClip(MovieClip *clip) { this->m_clip = clip; @@ -72,6 +76,9 @@ class MovieClipAttributeOperation : public NodeOperation { { this->m_invert = invert; } + + private: + void calc_value(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 7e896046f01..402d29893a4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -160,7 +160,7 @@ int get_datatype_size(DataType datatype) static float *init_buffer(unsigned int width, unsigned int height, DataType datatype) { - // When initializing the tree during initial load the width and height can be zero. + /* When initializing the tree during initial load the width and height can be zero. */ if (width != 0 && height != 0) { int size = get_datatype_size(datatype); return (float *)MEM_callocN(width * height * size * sizeof(float), "OutputFile buffer"); @@ -294,6 +294,22 @@ void OutputSingleLayerOperation::deinitExecution() this->m_imageInput = nullptr; } +void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (!m_outputBuffer) { + return; + } + + MemoryBuffer output_buf(m_outputBuffer, + COM_data_type_num_channels(this->m_datatype), + this->getWidth(), + this->getHeight()); + const MemoryBuffer *input_image = inputs[0]; + output_buf.copy_from(input_image, area); +} + /******************************* MultiLayer *******************************/ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_) @@ -444,4 +460,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } } +void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_image = inputs[0]; + for (int i = 0; i < this->m_layers.size(); i++) { + OutputOpenExrLayer &layer = this->m_layers[i]; + if (layer.outputBuffer) { + MemoryBuffer output_buf(layer.outputBuffer, + COM_data_type_num_channels(layer.datatype), + this->getWidth(), + this->getHeight()); + output_buf.copy_from(input_image, area); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 64ab4c06e7c..057cee0c43e 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "BLI_path_util.h" #include "BLI_rect.h" @@ -30,7 +30,7 @@ namespace blender::compositor { /* Writes the image to a single-layer file. */ -class OutputSingleLayerOperation : public NodeOperation { +class OutputSingleLayerOperation : public MultiThreadedOperation { protected: const RenderData *m_rd; const bNodeTree *m_tree; @@ -70,6 +70,10 @@ class OutputSingleLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; /* extra info for OpenEXR layers */ @@ -86,7 +90,7 @@ struct OutputOpenExrLayer { }; /* Writes inputs into OpenEXR multilayer channels. */ -class OutputOpenExrMultiLayerOperation : public NodeOperation { +class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { protected: const Scene *m_scene; const RenderData *m_rd; @@ -122,6 +126,10 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation { { return eCompositorPriority::Low; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; void add_exr_channels(void *exrhandle, diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index bfe735aab15..dbe45fa60db 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -24,7 +24,6 @@ SetColorOperation::SetColorOperation() { this->addOutputSocket(DataType::Color); flags.is_set_operation = true; - flags.is_fullframe_operation = true; } void SetColorOperation::executePixelSampled(float output[4], @@ -42,13 +41,4 @@ void SetColorOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } -void SetColorOperation::update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span<MemoryBuffer *> UNUSED(inputs)) -{ - BLI_assert(output->is_a_single_elem()); - float *out_elem = output->get_elem(area.xmin, area.ymin); - copy_v4_v4(out_elem, m_color); -} - } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index f4c0948ee1b..f546d5e7668 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -85,10 +85,6 @@ class SetColorOperation : public ConstantOperation { void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; - - void update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index c12fb106afd..ef43cf64653 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -24,7 +24,6 @@ SetValueOperation::SetValueOperation() { this->addOutputSocket(DataType::Value); flags.is_set_operation = true; - flags.is_fullframe_operation = true; } void SetValueOperation::executePixelSampled(float output[4], @@ -42,13 +41,4 @@ void SetValueOperation::determineResolution(unsigned int resolution[2], resolution[1] = preferredResolution[1]; } -void SetValueOperation::update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span<MemoryBuffer *> UNUSED(inputs)) -{ - BLI_assert(output->is_a_single_elem()); - float *out_elem = output->get_elem(area.xmin, area.ymin); - *out_elem = m_value; -} - } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index f18d44d9554..726624c1c6a 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -56,9 +56,6 @@ class SetValueOperation : public ConstantOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) override; - void update_memory_buffer(MemoryBuffer *output, - const rcti &area, - Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index a4754de370d..d18ed3b8e14 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -79,4 +79,17 @@ void SplitOperation::determineResolution(unsigned int resolution[2], NodeOperation::determineResolution(resolution, preferredResolution); } +void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : + this->m_splitPercentage * this->getHeight() / 100.0f; + const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent; + memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 09e48821dd0..2d09d2a07dc 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SplitOperation : public NodeOperation { +class SplitOperation : public MultiThreadedOperation { private: SocketReader *m_image1Input; SocketReader *m_image2Input; @@ -45,6 +45,10 @@ class SplitOperation : public NodeOperation { { this->m_xSplit = xsplit; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index bd82b6397ad..ad96e3a02ba 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -30,7 +30,7 @@ SunBeamsOperation::SunBeamsOperation() this->flags.complex = true; } -void SunBeamsOperation::initExecution() +void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); @@ -38,6 +38,11 @@ void SunBeamsOperation::initExecution() this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); } +void SunBeamsOperation::initExecution() +{ + calc_rays_common_data(); +} + /** * Defines a line accumulator for a specific sector, * given by the four matrix entries that rotate from buffer space into the sector @@ -140,7 +145,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y); + float *iter = input->getBuffer() + input->get_coords_offset(x, y); return iter; } @@ -159,7 +164,6 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { float dist_max) { const rcti &rect = input->get_rect(); - int buffer_width = input->getWidth(); int x, y, num; float v, dv; float falloff_factor; @@ -168,9 +172,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { zero_v4(output); if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { - copy_v4_v4(output, - input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * - ((int)source[0] + input->getWidth() * (int)source[1])); + copy_v4_v4(output, input->get_elem(source[0], source[1])); return; } @@ -210,7 +212,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { /* decrement u */ x -= fxu; y -= fyu; - buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxu * input->elem_stride + fyu * input->row_stride; /* decrement v (in steps of dv < 1) */ v_local -= dv; @@ -219,7 +221,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { x -= fxv; y -= fyv; - buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; + buffer -= fxv * input->elem_stride + fyv * input->row_stride; } } @@ -356,4 +358,39 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); } +void SunBeamsOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS(input_idx); + calc_rays_common_data(); + + r_input_area = output_area; + /* Enlarges the rect by moving each corner toward the source. + * This is the maximum distance that pixels can influence each other + * and gives a rect that contains all possible accumulated pixels. */ + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px); +} + +void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[0]; + float coords[2]; + for (int y = area.ymin; y < area.ymax; y++) { + coords[1] = y; + float *out_elem = output->get_elem(area.xmin, y); + for (int x = area.xmin; x < area.xmax; x++) { + coords[0] = x; + accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px); + out_elem += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index d3725021cde..71fc04453fe 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -17,11 +17,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class SunBeamsOperation : public NodeOperation { +class SunBeamsOperation : public MultiThreadedOperation { public: SunBeamsOperation(); @@ -40,6 +40,14 @@ class SunBeamsOperation : public NodeOperation { m_data = data; } + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + private: + void calc_rays_common_data(); + private: NodeSunBeams m_data; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 6bfacb0c75d..20da468eeb1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -17,6 +17,8 @@ */ #include "COM_TonemapOperation.h" +#include "COM_ExecutionSystem.h" + #include "BLI_math.h" #include "BLI_utildefines.h" @@ -153,4 +155,126 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) /* pass */ } +void TonemapOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + NodeOperation *operation = getInputOperation(input_idx); + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = operation->getWidth(); + r_input_area.ymax = operation->getHeight(); +} + +struct Luminance { + float sum; + float color_sum[3]; + float log_sum; + float min; + float max; + int num_pixels; +}; + +static Luminance calc_area_luminance(const MemoryBuffer *input, const rcti &area) +{ + Luminance lum = {0}; + for (const float *elem : input->get_buffer_area(area)) { + const float lu = IMB_colormanagement_get_luminance(elem); + lum.sum += lu; + add_v3_v3(lum.color_sum, elem); + lum.log_sum += logf(MAX2(lu, 0.0f) + 1e-5f); + lum.max = MAX2(lu, lum.max); + lum.min = MIN2(lu, lum.min); + lum.num_pixels++; + } + return lum; +} + +void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (this->m_cachedInstance == nullptr) { + Luminance lum = {0}; + const MemoryBuffer *input = inputs[0]; + exec_system_->execute_work<Luminance>( + input->get_rect(), + [=](const rcti &split) { return calc_area_luminance(input, split); }, + lum, + [](Luminance &join, const Luminance &chunk) { + join.sum += chunk.sum; + add_v3_v3(join.color_sum, chunk.color_sum); + join.log_sum += chunk.log_sum; + join.max = MAX2(join.max, chunk.max); + join.min = MIN2(join.min, chunk.min); + join.num_pixels += chunk.num_pixels; + }); + + AvgLogLum *avg = new AvgLogLum(); + avg->lav = lum.sum / lum.num_pixels; + mul_v3_v3fl(avg->cav, lum.color_sum, 1.0f / lum.num_pixels); + const float max_log = log((double)lum.max + 1e-5); + const float min_log = log((double)lum.min + 1e-5); + const float avg_log = lum.log_sum / lum.num_pixels; + avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; + const float al = exp((double)avg_log); + avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); + avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); + this->m_cachedInstance = avg; + } +} + +void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + AvgLogLum *avg = m_cachedInstance; + const float igm = avg->igm; + const float offset = this->m_data->offset; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + mul_v3_fl(it.out, avg->al); + float dr = it.out[0] + offset; + float dg = it.out[1] + offset; + float db = it.out[2] + offset; + it.out[0] /= ((dr == 0.0f) ? 1.0f : dr); + it.out[1] /= ((dg == 0.0f) ? 1.0f : dg); + it.out[2] /= ((db == 0.0f) ? 1.0f : db); + if (igm != 0.0f) { + it.out[0] = powf(MAX2(it.out[0], 0.0f), igm); + it.out[1] = powf(MAX2(it.out[1], 0.0f), igm); + it.out[2] = powf(MAX2(it.out[2], 0.0f), igm); + } + } +} + +void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + AvgLogLum *avg = m_cachedInstance; + NodeTonemap *ntm = this->m_data; + const float f = expf(-this->m_data->f); + const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); + const float ic = 1.0f - ntm->c; + const float ia = 1.0f - ntm->a; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + copy_v4_v4(it.out, it.in(0)); + const float L = IMB_colormanagement_get_luminance(it.out); + float I_l = it.out[0] + ic * (L - it.out[0]); + float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]); + float I_a = I_l + ia * (I_g - I_l); + it.out[0] /= (it.out[0] + powf(f * I_a, m)); + I_l = it.out[1] + ic * (L - it.out[1]); + I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]); + I_a = I_l + ia * (I_g - I_l); + it.out[1] /= (it.out[1] + powf(f * I_a, m)); + I_l = it.out[2] + ic * (L - it.out[2]); + I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]); + I_a = I_l + ia * (I_g - I_l); + it.out[2] /= (it.out[2] + powf(f * I_a, m)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 7ecb179504d..56b57730ec1 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -39,7 +39,7 @@ typedef struct AvgLogLum { * \brief base class of tonemap, implementing the simple tonemap * \ingroup operation */ -class TonemapOperation : public NodeOperation { +class TonemapOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -85,6 +85,14 @@ class TonemapOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; /** @@ -99,6 +107,10 @@ class PhotoreceptorTonemapOperation : public TonemapOperation { * The inner loop of this operation. */ void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 993410e3e84..0f4be16a620 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -42,14 +42,24 @@ TrackPositionOperation::TrackPositionOperation() this->m_relativeFrame = 0; this->m_speed_output = false; flags.is_set_operation = true; + is_track_position_calculated_ = false; } void TrackPositionOperation::initExecution() { + if (!is_track_position_calculated_) { + calc_track_position(); + } +} + +void TrackPositionOperation::calc_track_position() +{ + is_track_position_calculated_ = true; MovieTracking *tracking = nullptr; MovieClipUser user = {0}; MovieTrackingObject *object; + track_position_ = 0; zero_v2(this->m_markerPos); zero_v2(this->m_relativePos); @@ -114,6 +124,14 @@ void TrackPositionOperation::initExecution() } } } + + track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; + if (this->m_axis == 0) { + track_position_ *= this->m_width; + } + else { + track_position_ *= this->m_height; + } } void TrackPositionOperation::executePixelSampled(float output[4], @@ -131,6 +149,14 @@ void TrackPositionOperation::executePixelSampled(float output[4], } } +const float *TrackPositionOperation::get_constant_elem() +{ + if (!is_track_position_calculated_) { + calc_track_position(); + } + return &track_position_; +} + void TrackPositionOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]) { diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index b0b0a123bd6..f716bd97737 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -20,7 +20,7 @@ #include <string.h> -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" #include "DNA_movieclip_types.h" #include "DNA_tracking_types.h" @@ -33,7 +33,7 @@ namespace blender::compositor { /** * Class with implementation of green screen gradient rasterization */ -class TrackPositionOperation : public NodeOperation { +class TrackPositionOperation : public ConstantOperation { protected: MovieClip *m_movieClip; int m_framenumber; @@ -47,6 +47,8 @@ class TrackPositionOperation : public NodeOperation { int m_width, m_height; float m_markerPos[2]; float m_relativePos[2]; + float track_position_; + bool is_track_position_calculated_; /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -93,6 +95,11 @@ class TrackPositionOperation : public NodeOperation { void initExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + const float *get_constant_elem() override; + + private: + void calc_track_position(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index 9d53ed5d8ee..c2087fd071e 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -53,4 +53,14 @@ void VectorCurveOperation::deinitExecution() this->m_inputProgram = nullptr; } +void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + CurveMapping *curve_map = this->m_curveMapping; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 8cbb80e27c7..27b3ad69e17 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -47,6 +47,10 @@ class VectorCurveOperation : public CurveBaseOperation { * Deinitialize the execution */ void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index d0d2fcac3ac..888602114cc 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -57,20 +57,20 @@ void WrapOperation::executePixelSampled(float output[4], float x, float y, Pixel MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; switch (m_wrappingType) { case CMP_NODE_WRAP_NONE: - // Intentionally empty, originalXPos and originalYPos have been set before + /* Intentionally empty, originalXPos and originalYPos have been set before. */ break; case CMP_NODE_WRAP_X: - // wrap only on the x-axis + /* Wrap only on the x-axis. */ nx = this->getWrappedOriginalXPos(x); extend_x = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_Y: - // wrap only on the y-axis + /* Wrap only on the y-axis. */ ny = this->getWrappedOriginalYPos(y); extend_y = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_XY: - // wrap on both + /* Wrap on both. */ nx = this->getWrappedOriginalXPos(x); ny = this->getWrappedOriginalYPos(y); extend_x = MemoryBufferExtend::Repeat; @@ -92,7 +92,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, newInput.ymax = input->ymax; if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { - // wrap only on the x-axis if tile is wrapping + /* Wrap only on the x-axis if tile is wrapping. */ newInput.xmin = getWrappedOriginalXPos(input->xmin); newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); if (newInput.xmin >= newInput.xmax) { @@ -101,7 +101,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input, } } if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { - // wrap only on the y-axis if tile is wrapping + /* Wrap only on the y-axis if tile is wrapping. */ newInput.ymin = getWrappedOriginalYPos(input->ymin); newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); if (newInput.ymin >= newInput.ymax) { diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 9d3ca7e736e..7050c3b2d83 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -33,6 +33,7 @@ ZCombineOperation::ZCombineOperation() this->m_depth1Reader = nullptr; this->m_image2Reader = nullptr; this->m_depth2Reader = nullptr; + this->flags.can_be_constant = true; } void ZCombineOperation::initExecution() @@ -60,6 +61,19 @@ void ZCombineOperation::executePixelSampled(float output[4], this->m_image2Reader->readSampled(output, x, y, sampler); } } + +void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color = (depth1 < depth2) ? it.in(0) : it.in(2); + copy_v4_v4(it.out, color); + } +} + void ZCombineAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -88,6 +102,32 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float depth1 = *it.in(1); + const float depth2 = *it.in(3); + const float *color1; + const float *color2; + if (depth1 <= depth2) { + color1 = it.in(0); + color2 = it.in(2); + } + else { + color1 = it.in(2); + color2 = it.in(0); + } + const float fac = color1[3]; + const float ifac = 1.0f - fac; + it.out[0] = fac * color1[0] + ifac * color2[0]; + it.out[1] = fac * color1[1] + ifac * color2[1]; + it.out[2] = fac * color1[2] + ifac * color2[2]; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineOperation::deinitExecution() { this->m_image1Reader = nullptr; @@ -132,6 +172,18 @@ void ZCombineMaskOperation::executePixelSampled(float output[4], interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } +void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + interp_v4_v4v4(it.out, color1, color2, 1.0f - mask); + } +} + void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], float x, float y, @@ -154,6 +206,24 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], output[3] = MAX2(color1[3], color2[3]); } +void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float mask = *it.in(0); + const float *color1 = it.in(1); + const float *color2 = it.in(2); + const float fac = (1.0f - mask) * (1.0f - color1[3]) + mask * color2[3]; + const float mfac = 1.0f - fac; + + it.out[0] = color1[0] * mfac + color2[0] * fac; + it.out[1] = color1[1] * mfac + color2[1] * fac; + it.out[2] = color1[2] * mfac + color2[2] * fac; + it.out[3] = MAX2(color1[3], color2[3]); + } +} + void ZCombineMaskOperation::deinitExecution() { this->m_image1Reader = nullptr; diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index d0b1aee7310..acd60b6c866 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -26,7 +26,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ZCombineOperation : public NodeOperation { +class ZCombineOperation : public MultiThreadedOperation { protected: SocketReader *m_image1Reader; SocketReader *m_depth1Reader; @@ -46,13 +46,21 @@ class ZCombineOperation : public NodeOperation { * The inner loop of this operation. */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ZCombineAlphaOperation : public ZCombineOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; -class ZCombineMaskOperation : public NodeOperation { +class ZCombineMaskOperation : public MultiThreadedOperation { protected: SocketReader *m_maskReader; SocketReader *m_image1Reader; @@ -64,9 +72,17 @@ class ZCombineMaskOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class ZCombineMaskAlphaOperation : public ZCombineMaskOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/datatoc/datatoc.c b/source/blender/datatoc/datatoc.c index 62b4cee4af0..816353be9de 100644 --- a/source/blender/datatoc/datatoc.c +++ b/source/blender/datatoc/datatoc.c @@ -100,15 +100,14 @@ int main(int argc, char **argv) fprintf(fpout, "const int datatoc_%s_size = %d;\n", argv[1], (int)size); fprintf(fpout, "const char datatoc_%s[] = {\n", argv[1]); while (size--) { - /* if we want to open in an editor - * this is nicer to avoid very long lines */ -#ifdef VERBOSE + /* Even though this file is generated and doesn't need new-lines, + * these files may be loaded by developers when looking up symbols. + * Avoid a very long single line that may lock-up some editors. */ if (size % 32 == 31) { fprintf(fpout, "\n"); } -#endif - /* fprintf (fpout, "\\x%02x", getc(fpin)); */ + // fprintf(fpout, "\\x%02x", getc(fpin)); fprintf(fpout, "%3d,", getc(fpin)); } diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 749b1bba871..47dad17b482 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -85,7 +85,7 @@ extern "C" { // Get main depsgraph instance from context! /* Create new Depsgraph instance */ -// TODO: what args are needed here? What's the building-graph entry point? +/* TODO: what args are needed here? What's the building-graph entry point? */ Depsgraph *DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 8d1074d912f..22bce10937d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -648,9 +648,9 @@ void DepsgraphNodeBuilder::build_idproperties(IDProperty *id_property) void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collection, Collection *collection) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; - const bool is_collection_restricted = (collection->flag & restrict_flag); + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; + const bool is_collection_restricted = (collection->flag & visibility_flag); const bool is_collection_visible = !is_collection_restricted && is_parent_collection_visible_; IDNode *id_node; if (built_map_.checkIsBuiltAndTag(collection)) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index 29aa05b83db..b6e3f4fa935 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -60,11 +60,11 @@ namespace blender::deg { void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) { - if (lc->collection->flag & restrict_flag) { + if (lc->collection->flag & visibility_flag) { continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c7c6fafa512..d88e9bc9c04 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -118,6 +118,7 @@ #include "intern/node/deg_node_operation.h" #include "intern/node/deg_node_time.h" +#include "intern/depsgraph.h" #include "intern/depsgraph_relation.h" #include "intern/depsgraph_type.h" @@ -2095,7 +2096,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); } - if (BKE_object_modifier_use_time(object, md)) { + if (BKE_object_modifier_use_time(scene_, object, md, graph_->mode)) { TimeSourceKey time_src_key; add_relation(time_src_key, obdata_ubereval_key, "Time Source"); } @@ -2224,6 +2225,11 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE); add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done"); + + /* Link object data evaluation to parameter evaluation. */ + ComponentKey parameters_key(obdata, NodeType::PARAMETERS); + add_relation(parameters_key, obdata_geom_eval_key, "ObData Geom Params"); + /* Type-specific links. */ const ID_Type id_type = GS(obdata->name); switch (id_type) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index 24876049942..c37fb1b83a4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -61,11 +61,11 @@ namespace blender::deg { void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb) { - const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT : - COLLECTION_RESTRICT_RENDER; + const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT : + COLLECTION_HIDE_RENDER; for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) { - if ((lc->collection->flag & restrict_flag)) { + if (lc->collection->flag & visibility_flag) { continue; } if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index bdabd67cc07..40e59875832 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -400,8 +400,8 @@ RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id) bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop) { return prop != nullptr && RNA_property_is_idprop(prop) && - /* ID properties in the geometry nodes modifier don't affect that parameters node. Instead - they affect the modifier and therefore the geometry node directly. */ + /* ID properties in the geometry nodes modifier don't affect that parameters node. + * Instead they affect the modifier and therefore the geometry node directly. */ !RNA_struct_is_a(ptr->type, &RNA_NodesModifier); } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index ab93464d09a..dd96c5a3b2b 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -646,8 +646,8 @@ void graph_id_tag_update( { const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug; if (graph != nullptr && graph->is_evaluating) { - if (debug_flags & G_DEBUG_DEPSGRAPH) { - printf("ID tagged for update during dependency graph evaluation."); + if (debug_flags & G_DEBUG_DEPSGRAPH_TAG) { + printf("ID tagged for update during dependency graph evaluation.\n"); } return; } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index 255ea840088..d0bb841caab 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -68,11 +68,11 @@ ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, const IDNode *id_node); ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, struct ID *id_orig); -/* Helper function which frees memory used by copy-on-written databnlock. */ +/* Helper function which frees memory used by copy-on-written data-block. */ void deg_free_copy_on_write_datablock(struct ID *id_cow); /* Callback function for depsgraph operation node which ensures copy-on-write - * datablock is ready for use by further evaluation routines. + * data-block is ready for use by further evaluation routines. */ void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNode *id_node); @@ -80,16 +80,16 @@ void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNo * copies inside. */ bool deg_validate_copy_on_write_datablock(ID *id_cow); -/* Tag given ID block as being copy-on-wtritten. */ +/* Tag given ID block as being copy-on-written. */ void deg_tag_copy_on_write_id(struct ID *id_cow, const struct ID *id_orig); -/* Check whether ID datablock is expanded. +/* Check whether ID data-block is expanded. * * TODO(sergey): Make it an inline function or a macro. */ bool deg_copy_on_write_is_expanded(const struct ID *id_cow); -/* Check whether copy-on-write datablock is needed for given ID. +/* Check whether copy-on-write data-block is needed for given ID. * * There are some exceptions on data-blocks which are covered by dependency graph * but which we don't want to start duplicating. diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 930d82fa225..257eb80ae0b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -483,6 +483,13 @@ data_to_c_simple(engines/image/shaders/engine_image_vert.glsl SRC) list(APPEND INC ) +if(WITH_DRAW_DEBUG) + list(APPEND SRC + engines/select/select_debug_engine.c + ) + add_definitions(-DWITH_DRAW_DEBUG) +endif() + if(WITH_MOD_FLUID) list(APPEND INC ../../../intern/mantaflow/extern diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index f4dc553e982..879a7b08eba 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -246,7 +246,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity); BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state); - /* Do not fadeout when doing probe rendering, only when drawing the background */ + /* Do not fade-out when doing probe rendering, only when drawing the background. */ DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); } else { diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index a7ed6c777e8..3a2871249a2 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -132,6 +132,11 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_float_copy(grp, "normalSize", v3d->overlay.normals_length); DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + DRW_shgroup_uniform_bool_copy(grp, + "isConstantScreenSizeNormals", + (flag & V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS) != 0); + DRW_shgroup_uniform_float_copy( + grp, "normalScreenSize", v3d->overlay.normals_constant_screen_size); } { /* Mesh Analysis Pass */ diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 235104245cc..b07e86000fd 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -576,9 +576,20 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); - if (pd->ctx_mode == CTX_MODE_SCULPT) { - /* Sculpt overlays are drawn here to avoid artifacts with wireframe opacity. */ - OVERLAY_sculpt_draw(vedata); + /* These overlays are drawn here to avoid artifacts with wireframe opacity. */ + switch (pd->ctx_mode) { + case CTX_MODE_SCULPT: + OVERLAY_sculpt_draw(vedata); + break; + case CTX_MODE_EDIT_MESH: + case CTX_MODE_POSE: + case CTX_MODE_PAINT_WEIGHT: + case CTX_MODE_PAINT_VERTEX: + case CTX_MODE_PAINT_TEXTURE: + OVERLAY_paint_draw(vedata); + break; + default: + break; } if (DRW_state_is_fbo()) { @@ -601,11 +612,6 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_xray_depth_infront_copy(vedata); - if (pd->ctx_mode == CTX_MODE_PAINT_WEIGHT) { - /* Fix weird case where weightpaint mode needs to draw before xray bones. */ - OVERLAY_paint_draw(vedata); - } - if (DRW_state_is_fbo()) { GPU_framebuffer_bind(fbl->overlay_in_front_fb); } @@ -640,7 +646,6 @@ static void OVERLAY_draw_scene(void *vedata) switch (pd->ctx_mode) { case CTX_MODE_EDIT_MESH: - OVERLAY_paint_draw(vedata); OVERLAY_edit_mesh_draw(vedata); break; case CTX_MODE_EDIT_SURFACE: @@ -654,13 +659,8 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_edit_lattice_draw(vedata); break; case CTX_MODE_POSE: - OVERLAY_paint_draw(vedata); OVERLAY_pose_draw(vedata); break; - case CTX_MODE_PAINT_VERTEX: - case CTX_MODE_PAINT_TEXTURE: - OVERLAY_paint_draw(vedata); - break; case CTX_MODE_PARTICLE: OVERLAY_edit_particle_draw(vedata); break; diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index f5be9c846d1..2a9080eb217 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -1357,7 +1357,8 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, } } } - BKE_constraints_clear_evalob(cob); + /* NOTE: Don't use BKE_constraints_clear_evalob here as that will reset ob->constinv. */ + MEM_freeN(cob); } } diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c index d52640ed174..60a90616d29 100644 --- a/source/blender/draw/engines/overlay/overlay_paint.c +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -92,18 +92,26 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) case CTX_MODE_PAINT_WEIGHT: { opacity = is_edit_mode ? 1.0 : pd->overlay.weight_paint_mode_opacity; if (opacity > 0.0f) { - state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; - state |= pd->painting.alpha_blending ? DRW_STATE_BLEND_ALPHA : DRW_STATE_BLEND_MUL; + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); - sh = OVERLAY_shader_paint_weight(); + const bool do_shading = draw_ctx->v3d->shading.type != OB_WIRE; + + sh = OVERLAY_shader_paint_weight(do_shading); pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_bool_copy(grp, "drawContours", draw_contours); - DRW_shgroup_uniform_bool_copy(grp, "useAlphaBlend", pd->painting.alpha_blending); DRW_shgroup_uniform_float_copy(grp, "opacity", opacity); DRW_shgroup_uniform_texture(grp, "colorramp", G_draw.weight_ramp); + /* Arbitrary light to give a hint of the geometry behind the weights. */ + if (do_shading) { + float light_dir[3]; + copy_v3_fl3(light_dir, 0.0f, 0.5f, 0.86602f); + normalize_v3(light_dir); + DRW_shgroup_uniform_vec3_copy(grp, "light_dir", light_dir); + } + if (pd->painting.alpha_blending) { state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; DRW_PASS_CREATE(psl->paint_depth_ps, state | pd->clipping_state); @@ -257,17 +265,10 @@ void OVERLAY_paint_draw(OVERLAY_Data *vedata) OVERLAY_PassList *psl = vedata->psl; OVERLAY_FramebufferList *fbl = vedata->fbl; - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); if (DRW_state_is_fbo()) { - if (pd->painting.alpha_blending) { - GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb : - fbl->overlay_default_fb); - } - else { - /* Paint overlay needs final color because of multiply blend mode. */ - GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb); - } + GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb : + fbl->overlay_default_fb); } if (psl->paint_depth_ps) { diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 03bfaf56f24..68f60bee779 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -736,7 +736,7 @@ GPUShader *OVERLAY_shader_paint_face(void); GPUShader *OVERLAY_shader_paint_point(void); GPUShader *OVERLAY_shader_paint_texture(void); GPUShader *OVERLAY_shader_paint_vertcol(void); -GPUShader *OVERLAY_shader_paint_weight(void); +GPUShader *OVERLAY_shader_paint_weight(bool shading); GPUShader *OVERLAY_shader_paint_wire(void); GPUShader *OVERLAY_shader_particle_dot(void); GPUShader *OVERLAY_shader_particle_shape(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index 7a7ae9a921b..edf9148c8c0 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -211,7 +211,7 @@ typedef struct OVERLAY_Shaders { GPUShader *paint_point; GPUShader *paint_texture; GPUShader *paint_vertcol; - GPUShader *paint_weight; + GPUShader *paint_weight[2]; GPUShader *paint_wire; GPUShader *particle_dot; GPUShader *particle_shape; @@ -1334,13 +1334,14 @@ GPUShader *OVERLAY_shader_paint_vertcol(void) return sh_data->paint_vertcol; } -GPUShader *OVERLAY_shader_paint_weight(void) +GPUShader *OVERLAY_shader_paint_weight(const bool shading) { + int index = shading ? 1 : 0; const DRWContextState *draw_ctx = DRW_context_state_get(); const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - if (!sh_data->paint_weight) { - sh_data->paint_weight = GPU_shader_create_from_arrays({ + if (!sh_data->paint_weight[index]) { + sh_data->paint_weight[index] = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_globals_lib_glsl, datatoc_common_view_lib_glsl, @@ -1349,10 +1350,10 @@ GPUShader *OVERLAY_shader_paint_weight(void) .frag = (const char *[]){datatoc_common_globals_lib_glsl, datatoc_paint_weight_frag_glsl, NULL}, - .defs = (const char *[]){sh_cfg->def, NULL}, + .defs = (const char *[]){sh_cfg->def, shading ? "#define FAKE_SHADING\n" : "", NULL}, }); } - return sh_data->paint_weight; + return sh_data->paint_weight[index]; } GPUShader *OVERLAY_shader_paint_wire(void) diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl index 007495f84e0..d370943db03 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl @@ -1,5 +1,7 @@ uniform float normalSize; +uniform float normalScreenSize; +uniform bool isConstantScreenSizeNormals; uniform sampler2D depthTex; uniform float alpha = 1.0; @@ -49,11 +51,24 @@ void main() } vec3 n = normalize(normal_object_to_world(nor)); - vec3 world_pos = point_object_to_world(pos); if (gl_VertexID == 0) { - world_pos += n * normalSize; + if (isConstantScreenSizeNormals) { + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + float dist_fac = length(cameraPos - world_pos); + float cos_fac = dot(cameraForward, cameraVec(world_pos)); + world_pos += n * normalScreenSize * dist_fac * cos_fac * pixelFac * sizePixel; + } + else { + float frustrum_fac = mul_project_m4_v3_zfac(n) * sizePixel; + world_pos += n * normalScreenSize * frustrum_fac; + } + } + else { + world_pos += n * normalSize; + } } gl_Position = point_world_to_ndc(world_pos); diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl index 0020d76ed6a..8009713d655 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl @@ -1,12 +1,12 @@ in vec2 weight_interp; /* (weight, alert) */ +in float color_fac; out vec4 fragColor; uniform float opacity = 1.0; uniform sampler1D colorramp; -uniform bool useAlphaBlend = false; uniform bool drawContours = false; float contours(float value, float steps, float width_px, float max_rel_width, float gradient) @@ -68,6 +68,13 @@ vec4 contour_grid(float weight, float weight_gradient) return grid * clamp((weight_gradient - flt_eps) / flt_eps, 0.0, 1.0); } +vec4 apply_color_fac(vec4 color_in) +{ + vec4 color = color_in; + color.rgb = max(vec3(0.005), color_in.rgb) * color_fac; + return color; +} + void main() { float alert = weight_interp.y; @@ -75,12 +82,13 @@ void main() /* Missing vertex group alert color. Uniform in practice. */ if (alert > 1.1) { - color = colorVertexMissingData; + color = apply_color_fac(colorVertexMissingData); } /* Weights are available */ else { float weight = weight_interp.x; vec4 weight_color = texture(colorramp, weight, 0); + weight_color = apply_color_fac(weight_color); /* Contour display */ if (drawContours) { @@ -93,14 +101,9 @@ void main() } /* Zero weight alert color. Nonlinear blend to reduce impact. */ - color = mix(weight_color, colorVertexUnreferenced, alert * alert); + vec4 color_unreferenced = apply_color_fac(colorVertexUnreferenced); + color = mix(weight_color, color_unreferenced, alert * alert); } - if (useAlphaBlend) { - fragColor = vec4(color.rgb, opacity); - } - else { - /* mix with 1.0 -> is like opacity when using multiply blend mode */ - fragColor = vec4(mix(vec3(1.0), color.rgb, opacity), 1.0); - } + fragColor = vec4(color.rgb, opacity); } diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl index b3baa8c7b07..31b6dc42cf4 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl @@ -1,8 +1,13 @@ +#ifdef FAKE_SHADING +uniform vec3 light_dir; +#endif in float weight; in vec3 pos; +in vec3 nor; out vec2 weight_interp; /* (weight, alert) */ +out float color_fac; void main() { @@ -14,6 +19,16 @@ void main() /* Separate actual weight and alerts for independent interpolation */ weight_interp = max(vec2(weight, -weight), 0.0); + /* Saturate the weight to give a hint of the geometry behind the weights. */ +#ifdef FAKE_SHADING + vec3 view_normal = normalize(normal_object_to_view(nor)); + color_fac = abs(dot(view_normal, light_dir)); + color_fac = color_fac * 0.9 + 0.1; + +#else + color_fac = 1.0; +#endif + #ifdef USE_WORLD_CLIP_PLANES world_clip_planes_calc_clip_distance(world_pos); #endif diff --git a/source/blender/draw/engines/select/select_debug_engine.c b/source/blender/draw/engines/select/select_debug_engine.c new file mode 100644 index 00000000000..e9437c5ab92 --- /dev/null +++ b/source/blender/draw/engines/select/select_debug_engine.c @@ -0,0 +1,135 @@ +/* + * 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. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Engine for debugging the selection map drawing. + */ + +#include "DNA_ID.h" +#include "DNA_vec_types.h" + +#include "DRW_engine.h" +#include "DRW_select_buffer.h" + +#include "draw_cache.h" +#include "draw_manager.h" + +#include "select_engine.h" + +#define SELECT_DEBUG_ENGINE "SELECT_DEBUG_ENGINE" + +/* -------------------------------------------------------------------- */ +/** \name Structs and static variables + * \{ */ + +typedef struct SELECTIDDEBUG_PassList { + struct DRWPass *debug_pass; +} SELECTIDDEBUG_PassList; + +typedef struct SELECTIDDEBUG_Data { + void *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + SELECTIDDEBUG_PassList *psl; + DRWViewportEmptyList *stl; +} SELECTIDDEBUG_Data; + +static struct { + struct GPUShader *select_debug_sh; +} e_data = {{NULL}}; /* Engine data */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Engine Functions + * \{ */ + +static void select_debug_engine_init(void *vedata) +{ + SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl; + + if (!e_data.select_debug_sh) { + e_data.select_debug_sh = DRW_shader_create_fullscreen( + "uniform usampler2D image;" + "in vec4 uvcoordsvar;" + "out vec4 fragColor;" + "void main() {" + " uint px = texture(image, uvcoordsvar.xy).r;" + " fragColor = vec4(1.0, 1.0, 1.0, 0.0);" + " if (px != 0u) {" + " fragColor.a = 1.0;" + " px &= 0x3Fu;" + " fragColor.r = ((px >> 0) & 0x3u) / float(0x3u);" + " fragColor.g = ((px >> 2) & 0x3u) / float(0x3u);" + " fragColor.b = ((px >> 4) & 0x3u) / float(0x3u);" + " }" + "}\n", + NULL); + } + + psl->debug_pass = DRW_pass_create("Debug Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); + GPUTexture *texture_u32 = DRW_engine_select_texture_get(); + if (texture_u32) { + DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.select_debug_sh, psl->debug_pass); + DRW_shgroup_uniform_texture(shgrp, "image", texture_u32); + DRW_shgroup_call_procedural_triangles(shgrp, NULL, 1); + } +} + +static void select_debug_draw_scene(void *vedata) +{ + SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl; + DRW_draw_pass(psl->debug_pass); +} + +static void select_debug_engine_free(void) +{ + DRW_SHADER_FREE_SAFE(e_data.select_debug_sh); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Engine Type + * \{ */ + +static const DrawEngineDataSize select_debug_data_size = DRW_VIEWPORT_DATA_SIZE( + SELECTIDDEBUG_Data); + +DrawEngineType draw_engine_debug_select_type = { + NULL, + NULL, + N_("Select ID Debug"), + &select_debug_data_size, + &select_debug_engine_init, + &select_debug_engine_free, + NULL, + NULL, + NULL, + &select_debug_draw_scene, + NULL, + NULL, + NULL, + NULL, +}; + +/** \} */ + +#undef SELECT_DEBUG_ENGINE diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 86b4a0ac727..96ab8a28e09 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -193,7 +193,7 @@ static void select_cache_init(void *vedata) if (e_data.context.select_mode & SCE_SELECT_VERTEX) { DRW_PASS_CREATE(psl->select_id_vert_pass, state); pd->shgrp_vert = DRW_shgroup_create(sh->select_id_flat, psl->select_id_vert_pass); - DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex); + DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", 2 * G_draw.block.sizeVertex); } } diff --git a/source/blender/draw/engines/select/select_engine.h b/source/blender/draw/engines/select/select_engine.h index 2b35cf6bee5..d6192103178 100644 --- a/source/blender/draw/engines/select/select_engine.h +++ b/source/blender/draw/engines/select/select_engine.h @@ -22,9 +22,15 @@ #pragma once +/* select_engine.c */ extern DrawEngineType draw_engine_select_type; extern RenderEngineType DRW_engine_viewport_select_type; +#ifdef WITH_DRAW_DEBUG +/* select_debug_engine.c */ +extern DrawEngineType draw_engine_debug_select_type; +#endif + struct SELECTID_Context *DRW_select_engine_context_get(void); struct GPUFrameBuffer *DRW_engine_select_framebuffer_get(void); diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 453ef9c7f8e..e17bd7d9956 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -399,7 +399,7 @@ typedef struct WORKBENCH_ViewLayerData { /* inline helper functions */ BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd) { - if ((wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT)) { + if (wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT) { if (STUDIOLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) { return (wpd->studio_light->flag & STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS) != 0; } diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 336ccd40d5c..6ed4148a7fe 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -552,6 +552,9 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND; gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */ copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill); + /* Caps. */ + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; + gpd->runtime.sbuffer_gps = gps; } return gpd->runtime.sbuffer_gps; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 359788545e4..52b76733b78 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -95,7 +95,7 @@ #define MDEPS_CREATE(buff_name, ...) [_BUFFER_INDEX(buff_name)] = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__) -#define _MDEPS_CREATE_MAP1(a) g_buffer_desps[_BUFFER_INDEX(a)] +#define _MDEPS_CREATE_MAP1(a) g_buffer_deps[_BUFFER_INDEX(a)] #define _MDEPS_CREATE_MAP2(a, b) _MDEPS_CREATE_MAP1(a) | _MDEPS_CREATE_MAP1(b) #define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | _MDEPS_CREATE_MAP1(c) #define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | _MDEPS_CREATE_MAP1(d) @@ -110,8 +110,8 @@ #ifndef NDEBUG # define _MDEPS_ASSERT2(b, name) \ - g_buffer_desps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \ - BLI_assert(g_buffer_desps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b)) + g_buffer_deps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \ + BLI_assert(g_buffer_deps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b)) # define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2) # define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3) # define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4) @@ -120,7 +120,7 @@ # 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_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) -# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_desps_d[_BUFFER_INDEX(name)] == g_buffer_desps[_BUFFER_INDEX(name)]) +# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_deps_d[_BUFFER_INDEX(name)] == g_buffer_deps[_BUFFER_INDEX(name)]) #else # define MDEPS_ASSERT(...) # define MDEPS_ASSERT_MAP(name) @@ -128,7 +128,7 @@ /* clang-format on */ -static const DRWBatchFlag g_buffer_desps[] = { +static const DRWBatchFlag g_buffer_deps[] = { MDEPS_CREATE(vbo.pos_nor, batch.surface, batch.surface_weights, @@ -215,7 +215,7 @@ static const DRWBatchFlag g_buffer_desps[] = { }; #ifndef NDEBUG -static DRWBatchFlag g_buffer_desps_d[sizeof(g_buffer_desps)] = {0}; +static DRWBatchFlag g_buffer_deps_d[ARRAY_SIZE(g_buffer_deps)] = {0}; #endif static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 35072518b66..027ab8ce32b 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -512,7 +512,7 @@ static void drw_context_state_init(void) if (DST.draw_ctx.object_mode & OB_MODE_POSE) { DST.draw_ctx.object_pose = DST.draw_ctx.obact; } - else if ((DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT)) { + else if (DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT) { DST.draw_ctx.object_pose = BKE_object_pose_armature_get(DST.draw_ctx.obact); } else { @@ -1281,6 +1281,12 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), use_drw_engine(&draw_engine_gpencil_type); } drw_engines_enable_overlays(); + +#ifdef WITH_DRAW_DEBUG + if (G.debug_value == 31) { + use_drw_engine(&draw_engine_debug_select_type); + } +#endif } static void drw_engines_disable(void) @@ -2682,6 +2688,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_viewport_var_init(); /* Update UBO's */ + UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); DRW_globals_update(); /* Init Select Engine */ @@ -2939,6 +2946,9 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_overlay_type); DRW_engine_register(&draw_engine_select_type); DRW_engine_register(&draw_engine_basic_type); +#ifdef WITH_DRAW_DEBUG + DRW_engine_register(&draw_engine_debug_select_type); +#endif DRW_engine_register(&draw_engine_image_type); DRW_engine_register(DRW_engine_viewport_external_type.draw_engine); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 2126385a352..af331c86a8b 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -1533,13 +1533,6 @@ static void draw_frustum_boundbox_calc(const float (*viewinv)[4], projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far); - if (is_persp) { - left *= near; - right *= near; - bottom *= near; - top *= near; - } - r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near; r_bbox->vec[0][0] = r_bbox->vec[3][0] = left; r_bbox->vec[4][0] = r_bbox->vec[7][0] = right; diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 7f7696d485c..22356a3c57b 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -283,11 +283,11 @@ void DRW_state_reset_ex(DRWState state) static void drw_state_validate(void) { /* Cannot write to stencil buffer without stencil test. */ - if ((DST.state & DRW_STATE_WRITE_STENCIL_ENABLED)) { + if (DST.state & DRW_STATE_WRITE_STENCIL_ENABLED) { BLI_assert(DST.state & DRW_STATE_STENCIL_TEST_ENABLED); } /* Cannot write to depth buffer without depth test. */ - if ((DST.state & DRW_STATE_WRITE_DEPTH)) { + if (DST.state & DRW_STATE_WRITE_DEPTH) { BLI_assert(DST.state & DRW_STATE_DEPTH_TEST_ENABLED); } } diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 73afdd6e1e3..99e8ba968a2 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -83,8 +83,8 @@ GPUTexture *DRW_texture_create_1d(int w, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_1d(__func__, w, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_1d(__func__, w, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -93,8 +93,8 @@ GPUTexture *DRW_texture_create_1d(int w, GPUTexture *DRW_texture_create_2d( int w, int h, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -103,8 +103,8 @@ GPUTexture *DRW_texture_create_2d( GPUTexture *DRW_texture_create_2d_array( int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -113,9 +113,9 @@ GPUTexture *DRW_texture_create_2d_array( GPUTexture *DRW_texture_create_3d( int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; GPUTexture *tex = GPU_texture_create_3d( - __func__, w, h, d, mips, format, GPU_DATA_FLOAT, fpixels); + __func__, w, h, d, mip_len, format, GPU_DATA_FLOAT, fpixels); drw_texture_set_parameters(tex, flags); return tex; @@ -126,8 +126,8 @@ GPUTexture *DRW_texture_create_cube(int w, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_cube(__func__, w, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_cube(__func__, w, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; } @@ -135,8 +135,8 @@ GPUTexture *DRW_texture_create_cube(int w, GPUTexture *DRW_texture_create_cube_array( int w, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels) { - int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mips, format, fpixels); + int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1; + GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mip_len, format, fpixels); drw_texture_set_parameters(tex, flags); return tex; } diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index ae2c66881ff..90bb3762473 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -211,8 +211,17 @@ static bool is_cursor_visible_2d(const DRWContextState *draw_ctx) return false; } SpaceImage *sima = (SpaceImage *)space_data; - if (sima->mode != SI_MODE_UV) { - return false; + switch (sima->mode) { + case SI_MODE_VIEW: + return false; + break; + case SI_MODE_PAINT: + return false; + break; + case SI_MODE_MASK: + break; + case SI_MODE_UV: + break; } return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0; } diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index 0c7cbd4dac8..a9810b4cc77 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -267,7 +267,8 @@ static void test_overlay_glsl_shaders() EXPECT_NE(OVERLAY_shader_paint_point(), nullptr); EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr); EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr); - EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(false), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(true), nullptr); EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr); EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr); EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr); diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index 7a53b54b5a4..d9f52d90766 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -47,7 +47,7 @@ set(SRC keyframes_draw.c keyframes_edit.c keyframes_general.c - keyframes_keylist.c + keyframes_keylist.cc keyframing.c keyingsets.c time_scrub_ui.c diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index baf8adf28d0..6d272bfc180 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -494,17 +494,14 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ Object *ob = CTX_data_active_object(C); Mask *mask = CTX_data_edit_mask(C); bDopeSheet ads = {NULL}; - DLRBT_Tree keys; - ActKeyColumn *aknext, *akprev; + struct AnimKeylist *keylist = ED_keylist_create(); + const ActKeyColumn *aknext, *akprev; float cfranext, cfraprev; bool donenext = false, doneprev = false; int nextcount = 0, prevcount = 0; cfranext = cfraprev = (float)(CFRA); - /* init binarytree-list for getting keyframes */ - BLI_dlrbTree_init(&keys); - /* seed up dummy dopesheet context with flags to perform necessary filtering */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { /* only selected channels are included */ @@ -512,22 +509,23 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys, 0); - gpencil_to_keylist(&ads, scene->gpd, &keys, false); + scene_to_keylist(&ads, scene, keylist, 0); + gpencil_to_keylist(&ads, scene->gpd, keylist, false); if (ob) { - ob_to_keylist(&ads, ob, &keys, 0); - gpencil_to_keylist(&ads, ob->data, &keys, false); + ob_to_keylist(&ads, ob, keylist, 0); + gpencil_to_keylist(&ads, ob->data, keylist, false); } if (mask) { MaskLayer *masklay = BKE_mask_layer_active(mask); - mask_to_keylist(&ads, masklay, &keys); + mask_to_keylist(&ads, masklay, keylist); } + /* TODO(jbakker): Keylists are ordered, no need to do any searching at all. */ /* find matching keyframe in the right direction */ do { - aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext); + aknext = ED_keylist_find_next(keylist, cfranext); if (aknext) { if (CFRA == (int)aknext->cfra) { @@ -545,7 +543,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } while ((aknext != NULL) && (donenext == false)); do { - akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev); + akprev = ED_keylist_find_prev(keylist, cfraprev); if (akprev) { if (CFRA == (int)akprev->cfra) { @@ -562,7 +560,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ } while ((akprev != NULL) && (doneprev == false)); /* free temp stuff */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* any success? */ if (doneprev || donenext) { diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index b2d387ea898..020518b5813 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1892,7 +1892,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, } /* outliner restrict-flag */ - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { continue; } } @@ -3098,7 +3098,7 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m } /* outliner restrict-flag */ - if (ob->restrictflag & OB_RESTRICT_VIEWPORT) { + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { return false; } } diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 5992545bdbe..eda87cf1897 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -134,6 +134,28 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) else { structname = RNA_struct_ui_name(ptr.type); } + + /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but + * displaying the struct name alone is no meaningful information (and also cannot be + * filtered well), same for modifiers. So display strip name alongside as well. */ + if (GS(ptr.owner_id->name) == ID_SCE) { + if (BLI_str_startswith(fcu->rna_path, "sequence_editor.sequences_all[\"")) { + if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") || + strstr(fcu->rna_path, ".modifiers[")) { + char *stripname = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); + const char *structname_all = BLI_sprintfN( + "%s : %s", stripname ? stripname : "", structname); + if (free_structname) { + MEM_freeN((void *)structname); + } + if (stripname) { + MEM_freeN(stripname); + } + structname = structname_all; + free_structname = 1; + } + } + } } /* Property Name is straightforward */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index cfcea950955..0de3f429bc7 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -445,7 +445,7 @@ static void draw_marker_name(const uchar *text_color, if (marker->camera) { Object *camera = marker->camera; name = camera->id.name + 2; - if (camera->restrictflag & OB_RESTRICT_RENDER) { + if (camera->visibility_flag & OB_HIDE_RENDER) { final_text_color[3] = 100; } } diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index bddd5dbff55..d976f5f72ad 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -55,7 +55,7 @@ typedef struct MPathTarget { bMotionPath *mpath; /* motion path in question */ - DLRBT_Tree keys; /* temp, to know where the keyframes are */ + struct AnimKeylist *keylist; /* temp, to know where the keyframes are */ /* Original (Source Objects) */ Object *ob; /* source object */ @@ -187,7 +187,7 @@ static void motionpaths_calc_bake_targets(ListBase *targets, int cframe) float mframe = (float)(cframe); /* Tag if it's a keyframe */ - if (BLI_dlrbTree_search_exact(&mpt->keys, compare_ak_cfraPtr, &mframe)) { + if (ED_keylist_find_exact(mpt->keylist, mframe)) { mpv->flag |= MOTIONPATH_VERT_KEY; } else { @@ -234,52 +234,55 @@ static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int } } -static int motionpath_get_prev_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +/* TODO(jbakker): Remove complexity, keylists are ordered. */ +static int motionpath_get_prev_keyframe(MPathTarget *mpt, + struct AnimKeylist *keylist, + int current_frame) { if (current_frame <= mpt->mpath->start_frame) { return mpt->mpath->start_frame; } float current_frame_float = current_frame; - DLRBT_Node *node = BLI_dlrbTree_search_prev(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); - if (node == NULL) { + const ActKeyColumn *ak = ED_keylist_find_prev(keylist, current_frame_float); + if (ak == NULL) { return mpt->mpath->start_frame; } - ActKeyColumn *key_data = (ActKeyColumn *)node; - return key_data->cfra; + return ak->cfra; } static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt, - DLRBT_Tree *fcu_keys, + struct AnimKeylist *keylist, int current_frame) { - int frame = motionpath_get_prev_keyframe(mpt, fcu_keys, current_frame); - return motionpath_get_prev_keyframe(mpt, fcu_keys, frame); + int frame = motionpath_get_prev_keyframe(mpt, keylist, current_frame); + return motionpath_get_prev_keyframe(mpt, keylist, frame); } -static int motionpath_get_next_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame) +static int motionpath_get_next_keyframe(MPathTarget *mpt, + struct AnimKeylist *keylist, + int current_frame) { if (current_frame >= mpt->mpath->end_frame) { return mpt->mpath->end_frame; } float current_frame_float = current_frame; - DLRBT_Node *node = BLI_dlrbTree_search_next(fcu_keys, compare_ak_cfraPtr, ¤t_frame_float); - if (node == NULL) { + const ActKeyColumn *ak = ED_keylist_find_next(keylist, current_frame_float); + if (ak == NULL) { return mpt->mpath->end_frame; } - ActKeyColumn *key_data = (ActKeyColumn *)node; - return key_data->cfra; + return ak->cfra; } static int motionpath_get_next_next_keyframe(MPathTarget *mpt, - DLRBT_Tree *fcu_keys, + struct AnimKeylist *keylist, int current_frame) { - int frame = motionpath_get_next_keyframe(mpt, fcu_keys, current_frame); - return motionpath_get_next_keyframe(mpt, fcu_keys, frame); + int frame = motionpath_get_next_keyframe(mpt, keylist, current_frame); + return motionpath_get_next_keyframe(mpt, keylist, frame); } static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt), @@ -324,17 +327,16 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, * Could be optimized further by storing some flags about which channels has been modified so * we ignore all others (which can potentially make an update range unnecessary wide). */ for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { - DLRBT_Tree fcu_keys; - BLI_dlrbTree_init(&fcu_keys); - fcurve_to_keylist(adt, fcu, &fcu_keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + fcurve_to_keylist(adt, fcu, keylist, 0); - int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, current_frame); - int fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, current_frame); + int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame); + int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame); /* Extend range further, since acceleration compensation propagates even further away. */ if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { - fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, fcu_sfra); - fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, fcu_efra); + fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, fcu_sfra); + fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, fcu_efra); } if (fcu_sfra <= fcu_efra) { @@ -342,14 +344,14 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, *r_efra = max_ii(*r_efra, fcu_efra); } - BLI_dlrbTree_free(&fcu_keys); + ED_keylist_free(keylist); } } static void motionpath_free_free_tree_data(ListBase *targets) { LISTBASE_FOREACH (MPathTarget *, mpt, targets) { - BLI_dlrbTree_free(&mpt->keys); + ED_keylist_free(mpt->keylist); } } @@ -418,7 +420,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id); /* build list of all keyframes in active action for object or pchan */ - BLI_dlrbTree_init(&mpt->keys); + mpt->keylist = ED_keylist_create(); ListBase *fcurve_list = NULL; if (adt) { @@ -433,12 +435,12 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, if (agrp) { fcurve_list = &agrp->channels; - agroup_to_keylist(adt, agrp, &mpt->keys, 0); + agroup_to_keylist(adt, agrp, mpt->keylist, 0); } } else { fcurve_list = &adt->action->curves; - action_to_keylist(adt, adt->action, &mpt->keys, 0); + action_to_keylist(adt, adt->action, mpt->keylist, 0); } } @@ -502,7 +504,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, avs->recalc &= ~ANIMVIZ_RECALC_PATHS; /* Clean temp data */ - BLI_dlrbTree_free(&mpt->keys); + ED_keylist_free(mpt->keylist); /* Free previous batches to force update. */ GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo); diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 31552330071..40871fba2be 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -672,10 +672,7 @@ static void fmod_envelope_deletepoint_cb(bContext *UNUSED(C), void *fcm_dv, void } else { /* just free array, since the only vert was deleted */ - if (env->data) { - MEM_freeN(env->data); - env->data = NULL; - } + MEM_SAFE_FREE(env->data); env->totvert = 0; } } diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 4f512c9d7ca..61918871b90 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -25,6 +25,8 @@ #include <float.h> +#include "MEM_guardedalloc.h" + #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" @@ -55,11 +57,7 @@ void draw_keyframe_shape(float x, short key_type, short mode, float alpha, - uint pos_id, - uint size_id, - uint color_id, - uint outline_color_id, - uint flags_id, + const KeyframeShaderBindings *sh_bindings, short handle_type, short extreme_type) { @@ -176,354 +174,563 @@ void draw_keyframe_shape(float x, } } - immAttr1f(size_id, size); - immAttr4ubv(color_id, fill_col); - immAttr4ubv(outline_color_id, outline_col); - immAttr1u(flags_id, flags); - immVertex2f(pos_id, x, y); + immAttr1f(sh_bindings->size_id, size); + immAttr4ubv(sh_bindings->color_id, fill_col); + immAttr4ubv(sh_bindings->outline_color_id, outline_col); + immAttr1u(sh_bindings->flags_id, flags); + immVertex2f(sh_bindings->pos_id, x, y); } -static void draw_keylist(View2D *v2d, - DLRBT_Tree *keys, - float ypos, - float yscale_fac, - bool channelLocked, - int saction_flag) -{ - if (keys == NULL) { - return; - } - - const float icon_sz = U.widget_unit * 0.5f * yscale_fac; - const float half_icon_sz = 0.5f * icon_sz; - const float smaller_sz = 0.35f * icon_sz; - const float ipo_sz = 0.1f * icon_sz; - const float gpencil_sz = smaller_sz * 0.8f; - const float screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); +/* Common attributes shared between the draw calls. */ +typedef struct DrawKeylistUIData { + float alpha; + float icon_sz; + float half_icon_sz; + float smaller_sz; + float ipo_sz; + float gpencil_sz; + float screenspace_margin; + float sel_color[4]; + float unsel_color[4]; + float sel_mhcol[4]; + float unsel_mhcol[4]; + float ipo_color[4]; + float ipo_color_mix[4]; + /* Show interpolation and handle type? */ + bool show_ipo; +} DrawKeylistUIData; + +static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx, + View2D *v2d, + float yscale_fac, + bool channel_locked, + eSAction_Flag saction_flag) +{ /* locked channels are less strongly shown, as feedback for locked channels in DopeSheet */ /* TODO: allow this opacity factor to be themed? */ - float alpha = channelLocked ? 0.25f : 1.0f; + ctx->alpha = channel_locked ? 0.25f : 1.0f; + + ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac; + ctx->half_icon_sz = 0.5f * ctx->icon_sz; + ctx->smaller_sz = 0.35f * ctx->icon_sz; + ctx->ipo_sz = 0.1f * ctx->icon_sz; + ctx->gpencil_sz = ctx->smaller_sz * 0.8f; + ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d); + + ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; + + UI_GetThemeColor4fv(TH_STRIP_SELECT, ctx->sel_color); + UI_GetThemeColor4fv(TH_STRIP, ctx->unsel_color); + UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ctx->ipo_color); + + ctx->sel_color[3] *= ctx->alpha; + ctx->unsel_color[3] *= ctx->alpha; + ctx->ipo_color[3] *= ctx->alpha; + + copy_v4_v4(ctx->sel_mhcol, ctx->sel_color); + ctx->sel_mhcol[3] *= 0.8f; + copy_v4_v4(ctx->unsel_mhcol, ctx->unsel_color); + ctx->unsel_mhcol[3] *= 0.8f; + copy_v4_v4(ctx->ipo_color_mix, ctx->ipo_color); + ctx->ipo_color_mix[3] *= 0.5f; +} - /* Show interpolation and handle type? */ - bool show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0; - /* draw keyblocks */ - float sel_color[4], unsel_color[4]; - float sel_mhcol[4], unsel_mhcol[4]; - float ipo_color[4], ipo_color_mix[4]; - - /* cache colors first */ - UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color); - UI_GetThemeColor4fv(TH_STRIP, unsel_color); - UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ipo_color); - - sel_color[3] *= alpha; - unsel_color[3] *= alpha; - ipo_color[3] *= alpha; - - copy_v4_v4(sel_mhcol, sel_color); - sel_mhcol[3] *= 0.8f; - copy_v4_v4(unsel_mhcol, unsel_color); - unsel_mhcol[3] *= 0.8f; - copy_v4_v4(ipo_color_mix, ipo_color); - ipo_color_mix[3] *= 0.5f; - - LISTBASE_FOREACH (ActKeyColumn *, ab, keys) { - /* Draw grease pencil bars between keyframes. */ - if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) { - UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); - float size = 1.0f; - switch (ab->next->key_type) { - case BEZT_KEYTYPE_BREAKDOWN: - case BEZT_KEYTYPE_MOVEHOLD: - case BEZT_KEYTYPE_JITTER: - size *= 0.5f; - break; - case BEZT_KEYTYPE_KEYFRAME: - size *= 0.8f; - break; - default: - break; - } - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = min_ff(ab->next->cfra - (screenspace_margin * size), ab->next->cfra), - .ymin = ypos - gpencil_sz, - .ymax = ypos + gpencil_sz, - }, - true, - 0.25f * (float)UI_UNIT_X, - (ab->block.sel) ? sel_mhcol : unsel_mhcol); - } - else { - /* Draw other types. */ - UI_draw_roundbox_corner_set(UI_CNR_NONE); - - int valid_hold = actkeyblock_get_valid_hold(ab); - if (valid_hold != 0) { - if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { - /* draw "moving hold" long-keyframe block - slightly smaller */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - smaller_sz, - .ymax = ypos + smaller_sz, - }, - true, - 3.0f, - (ab->block.sel) ? sel_mhcol : unsel_mhcol); - } - else { - /* draw standard long-keyframe block */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - half_icon_sz, - .ymax = ypos + half_icon_sz, - }, - true, - 3.0f, - (ab->block.sel) ? sel_color : unsel_color); - } - } - if (show_ipo && actkeyblock_is_valid(ab) && (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { - /* draw an interpolation line */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = ab->cfra, - .xmax = ab->next->cfra, - .ymin = ypos - ipo_sz, - .ymax = ypos + ipo_sz, - }, - true, - 3.0f, - (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ipo_color_mix : ipo_color); - } - } +static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT); + float size = 1.0f; + switch (ab->next->key_type) { + case BEZT_KEYTYPE_BREAKDOWN: + case BEZT_KEYTYPE_MOVEHOLD: + case BEZT_KEYTYPE_JITTER: + size *= 0.5f; + break; + case BEZT_KEYTYPE_KEYFRAME: + size *= 0.8f; + break; + default: + break; } + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra), + .ymin = ypos - ctx->gpencil_sz, + .ymax = ypos + ctx->gpencil_sz, + }, + true, + 0.25f * (float)UI_UNIT_X, + (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol); +} - GPU_blend(GPU_BLEND_ALPHA); +static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ - /* count keys */ - uint key_len = 0; - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { - /* Optimization: if keyframe doesn't appear within 5 units (screenspace) - * in visible area, don't draw. - * This might give some improvements, - * since we current have to flip between view/region matrices. - */ - if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) { - key_len++; - } - } + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->smaller_sz, + .ymax = ypos + ctx->smaller_sz, + }, + true, + 3.0f, + (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol); +} + +static void draw_keylist_block_standard(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->half_icon_sz, + .ymax = ypos + ctx->half_icon_sz, + }, + true, + 3.0f, + (ab->block.sel) ? ctx->sel_color : ctx->unsel_color); +} + +static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx, + const ActKeyColumn *ab, + float ypos) +{ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = ab->cfra, + .xmax = ab->next->cfra, + .ymin = ypos - ctx->ipo_sz, + .ymax = ypos + ctx->ipo_sz, + }, + true, + 3.0f, + (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ctx->ipo_color_mix : ctx->ipo_color); +} + +static void draw_keylist_block(const DrawKeylistUIData *ctx, const ActKeyColumn *ab, float ypos) +{ - if (key_len > 0) { - /* draw keys */ - GPUVertFormat *format = immVertexFormat(); - uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( - format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( - format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); - - GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); - immUniform1f("outline_scale", 1.0f); - immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); - immBegin(GPU_PRIM_POINTS, key_len); - - short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; - - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { - if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) { - if (show_ipo) { - handle_type = ak->handle_type; - } - if (saction_flag & SACTION_SHOW_EXTREMES) { - extreme_type = ak->extreme_type; - } - - draw_keyframe_shape(ak->cfra, - ypos, - icon_sz, - (ak->sel & SELECT), - ak->key_type, - KEYFRAME_SHAPE_BOTH, - alpha, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, - handle_type, - extreme_type); + /* Draw grease pencil bars between keyframes. */ + if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) { + draw_keylist_block_gpencil(ctx, ab, ypos); + } + else { + /* Draw other types. */ + UI_draw_roundbox_corner_set(UI_CNR_NONE); + + int valid_hold = actkeyblock_get_valid_hold(ab); + if (valid_hold != 0) { + if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { + /* draw "moving hold" long-keyframe block - slightly smaller */ + draw_keylist_block_moving_hold(ctx, ab, ypos); + } + else { + /* draw standard long-keyframe block */ + draw_keylist_block_standard(ctx, ab, ypos); } } - - immEnd(); - GPU_program_point_size(false); - immUnbindProgram(); + if (ctx->show_ipo && actkeyblock_is_valid(ab) && + (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) { + /* draw an interpolation line */ + draw_keylist_block_interpolation_line(ctx, ab, ypos); + } } - - GPU_blend(GPU_BLEND_NONE); } -/* *************************** Channel Drawing Funcs *************************** */ - -void draw_summary_channel( - View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac, int saction_flag) +static void draw_keylist_blocks(const DrawKeylistUIData *ctx, + const ListBase * /*ActKeyColumn*/ columns, + float ypos) { - DLRBT_Tree keys; - - saction_flag &= ~SACTION_SHOW_EXTREMES; + LISTBASE_FOREACH (ActKeyColumn *, ab, columns) { + draw_keylist_block(ctx, ab, ypos); + } +} - BLI_dlrbTree_init(&keys); +static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *ak) +{ + return IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax); +} - summary_to_keylist(ac, &keys, saction_flag); +static void draw_keylist_keys(const DrawKeylistUIData *ctx, + View2D *v2d, + const KeyframeShaderBindings *sh_bindings, + const ListBase * /*ActKeyColumn*/ keys, + float ypos, + eSAction_Flag saction_flag) +{ + short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); + LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + if (draw_keylist_is_visible_key(v2d, ak)) { + if (ctx->show_ipo) { + handle_type = ak->handle_type; + } + if (saction_flag & SACTION_SHOW_EXTREMES) { + extreme_type = ak->extreme_type; + } - BLI_dlrbTree_free(&keys); + draw_keyframe_shape(ak->cfra, + ypos, + ctx->icon_sz, + (ak->sel & SELECT), + ak->key_type, + KEYFRAME_SHAPE_BOTH, + ctx->alpha, + sh_bindings, + handle_type, + extreme_type); + } + } } -void draw_scene_channel( - View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac, int saction_flag) +/* *************************** Drawing Stack *************************** */ +typedef enum eAnimKeylistDrawListElemType { + ANIM_KEYLIST_SUMMARY, + ANIM_KEYLIST_SCENE, + ANIM_KEYLIST_OBJECT, + ANIM_KEYLIST_FCURVE, + ANIM_KEYLIST_ACTION, + ANIM_KEYLIST_AGROUP, + ANIM_KEYLIST_GP_LAYER, + ANIM_KEYLIST_MASK_LAYER, +} eAnimKeylistDrawListElemType; + +typedef struct AnimKeylistDrawListElem { + struct AnimKeylistDrawListElem *next, *prev; + struct AnimKeylist *keylist; + eAnimKeylistDrawListElemType type; + + float yscale_fac; + float ypos; + eSAction_Flag saction_flag; + bool channel_locked; + + bAnimContext *ac; + bDopeSheet *ads; + Scene *sce; + Object *ob; + AnimData *adt; + FCurve *fcu; + bAction *act; + bActionGroup *agrp; + bGPDlayer *gpl; + MaskLayer *masklay; + +} AnimKeylistDrawListElem; + +static void ED_keylist_draw_list_elem_build_keylist(AnimKeylistDrawListElem *elem) { - DLRBT_Tree keys; + switch (elem->type) { + case ANIM_KEYLIST_SUMMARY: { + summary_to_keylist(elem->ac, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_SCENE: { + scene_to_keylist(elem->ads, elem->sce, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_OBJECT: { + ob_to_keylist(elem->ads, elem->ob, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_FCURVE: { + fcurve_to_keylist(elem->adt, elem->fcu, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_ACTION: { + action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_AGROUP: { + agroup_to_keylist(elem->adt, elem->agrp, elem->keylist, elem->saction_flag); + break; + } + case ANIM_KEYLIST_GP_LAYER: { + gpl_to_keylist(elem->ads, elem->gpl, elem->keylist); + break; + } + case ANIM_KEYLIST_MASK_LAYER: { + mask_to_keylist(elem->ads, elem->masklay, elem->keylist); + break; + } + } +} - saction_flag &= ~SACTION_SHOW_EXTREMES; +static void ED_keylist_draw_list_elem_draw_blocks(AnimKeylistDrawListElem *elem, View2D *v2d) +{ + DrawKeylistUIData ctx; + draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); - BLI_dlrbTree_init(&keys); + const ListBase *keys = ED_keylist_listbase(elem->keylist); + draw_keylist_blocks(&ctx, keys, elem->ypos); +} - scene_to_keylist(ads, sce, &keys, saction_flag); +static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, + View2D *v2d, + const KeyframeShaderBindings *sh_bindings) +{ + DrawKeylistUIData ctx; + draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); + const ListBase *keys = ED_keylist_listbase(elem->keylist); + draw_keylist_keys(&ctx, v2d, sh_bindings, keys, elem->ypos, elem->saction_flag); +} - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); +typedef struct AnimKeylistDrawList { + ListBase /* AnimKeylistDrawListElem*/ channels; +} AnimKeylistDrawList; - BLI_dlrbTree_free(&keys); +AnimKeylistDrawList *ED_keylist_draw_list_create(void) +{ + return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); } -void draw_object_channel( - View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac, int saction_flag) +static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { - DLRBT_Tree keys; - - saction_flag &= ~SACTION_SHOW_EXTREMES; - - BLI_dlrbTree_init(&keys); + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_build_keylist(elem); + } +} - ob_to_keylist(ads, ob, &keys, saction_flag); +static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_draw_blocks(elem, v2d); + } +} - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); +static int ED_keylist_draw_keylist_visible_key_len(const View2D *v2d, + const ListBase * /*ActKeyColumn*/ keys) +{ + /* count keys */ + uint len = 0; - BLI_dlrbTree_free(&keys); + LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + /* Optimization: if keyframe doesn't appear within 5 units (screenspace) + * in visible area, don't draw. + * This might give some improvements, + * since we current have to flip between view/region matrices. + */ + if (draw_keylist_is_visible_key(v2d, ak)) { + len++; + } + } + return len; } -void draw_fcurve_channel( - View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac, int saction_flag) +static int ED_keylist_draw_list_visible_key_len(const AnimKeylistDrawList *draw_list, + const View2D *v2d) { - DLRBT_Tree keys; + uint len = 0; + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + const ListBase *keys = ED_keylist_listbase(elem->keylist); + len += ED_keylist_draw_keylist_visible_key_len(v2d, keys); + } + return len; +} - bool locked = (fcu->flag & FCURVE_PROTECTED) || - ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || - ((adt && adt->action) && ID_IS_LINKED(adt->action)); +static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + const int visible_key_len = ED_keylist_draw_list_visible_key_len(draw_list, v2d); + if (visible_key_len == 0) { + return; + } - BLI_dlrbTree_init(&keys); + GPU_blend(GPU_BLEND_ALPHA); - fcurve_to_keylist(adt, fcu, &keys, saction_flag); + GPUVertFormat *format = immVertexFormat(); + KeyframeShaderBindings sh_bindings; + + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + sh_bindings.outline_color_id = GPU_vertformat_attr_add( + format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immUniform1f("outline_scale", 1.0f); + immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); + immBegin(GPU_PRIM_POINTS, visible_key_len); + + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_draw_keys(elem, v2d, &sh_bindings); + } - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); + immEnd(); + GPU_program_point_size(false); + immUnbindProgram(); - BLI_dlrbTree_free(&keys); + GPU_blend(GPU_BLEND_NONE); } -void draw_agroup_channel( - View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag) +static void ED_keylist_draw_list_draw(AnimKeylistDrawList *draw_list, View2D *v2d) { - DLRBT_Tree keys; + ED_keylist_draw_list_draw_blocks(draw_list, v2d); + ED_keylist_draw_list_draw_keys(draw_list, v2d); +} - bool locked = (agrp->flag & AGRP_PROTECTED) || - ((adt && adt->action) && ID_IS_LINKED(adt->action)); +void ED_keylist_draw_list_flush(AnimKeylistDrawList *draw_list, View2D *v2d) +{ + ED_keylist_draw_list_build_keylists(draw_list); + ED_keylist_draw_list_draw(draw_list, v2d); +} - BLI_dlrbTree_init(&keys); +void ED_keylist_draw_list_free(AnimKeylistDrawList *draw_list) +{ + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_free(elem->keylist); + } + BLI_freelistN(&draw_list->channels); + MEM_freeN(draw_list); +} - agroup_to_keylist(adt, agrp, &keys, saction_flag); +static AnimKeylistDrawListElem *ed_keylist_draw_list_add_elem( + AnimKeylistDrawList *draw_list, + eAnimKeylistDrawListElemType elem_type, + float ypos, + float yscale_fac, + eSAction_Flag saction_flag) +{ + AnimKeylistDrawListElem *draw_elem = MEM_callocN(sizeof(AnimKeylistDrawListElem), __func__); + BLI_addtail(&draw_list->channels, draw_elem); + draw_elem->type = elem_type; + draw_elem->keylist = ED_keylist_create(); + draw_elem->ypos = ypos; + draw_elem->yscale_fac = yscale_fac; + draw_elem->saction_flag = saction_flag; + return draw_elem; +} - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); +/* *************************** Channel Drawing Funcs *************************** */ - BLI_dlrbTree_free(&keys); +void draw_summary_channel(struct AnimKeylistDrawList *draw_list, + bAnimContext *ac, + float ypos, + float yscale_fac, + int saction_flag) +{ + saction_flag &= ~SACTION_SHOW_EXTREMES; + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_SUMMARY, ypos, yscale_fac, saction_flag); + draw_elem->ac = ac; } -void draw_action_channel( - View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac, int saction_flag) +void draw_scene_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + Scene *sce, + float ypos, + float yscale_fac, + int saction_flag) { - DLRBT_Tree keys; - - bool locked = (act && ID_IS_LINKED(act)); - saction_flag &= ~SACTION_SHOW_EXTREMES; + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_SCENE, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->sce = sce; +} - BLI_dlrbTree_init(&keys); +void draw_object_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + Object *ob, + float ypos, + float yscale_fac, + int saction_flag) +{ + saction_flag &= ~SACTION_SHOW_EXTREMES; + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_OBJECT, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->ob = ob; +} - action_to_keylist(adt, act, &keys, saction_flag); +void draw_fcurve_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + FCurve *fcu, + float ypos, + float yscale_fac, + int saction_flag) +{ + const bool locked = (fcu->flag & FCURVE_PROTECTED) || + ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || + ((adt && adt->action) && ID_IS_LINKED(adt->action)); + + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_FCURVE, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->fcu = fcu; + draw_elem->channel_locked = locked; +} - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); +void draw_agroup_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + bActionGroup *agrp, + float ypos, + float yscale_fac, + int saction_flag) +{ + bool locked = (agrp->flag & AGRP_PROTECTED) || + ((adt && adt->action) && ID_IS_LINKED(adt->action)); - BLI_dlrbTree_free(&keys); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_AGROUP, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->agrp = agrp; + draw_elem->channel_locked = locked; } -void draw_gpencil_channel( - View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac, int saction_flag) +void draw_action_channel(AnimKeylistDrawList *draw_list, + AnimData *adt, + bAction *act, + float ypos, + float yscale_fac, + int saction_flag) { - DLRBT_Tree keys; - + const bool locked = (act && ID_IS_LINKED(act)); saction_flag &= ~SACTION_SHOW_EXTREMES; - BLI_dlrbTree_init(&keys); - - gpencil_to_keylist(ads, gpd, &keys, false); - - draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag); - - BLI_dlrbTree_free(&keys); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_ACTION, ypos, yscale_fac, saction_flag); + draw_elem->adt = adt; + draw_elem->act = act; + draw_elem->channel_locked = locked; } -void draw_gpl_channel( - View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag) +void draw_gpl_channel(AnimKeylistDrawList *draw_list, + bDopeSheet *ads, + bGPDlayer *gpl, + float ypos, + float yscale_fac, + int saction_flag) { - DLRBT_Tree keys; - bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0; - - BLI_dlrbTree_init(&keys); - - gpl_to_keylist(ads, gpl, &keys); - - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); - - BLI_dlrbTree_free(&keys); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_GP_LAYER, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->gpl = gpl; + draw_elem->channel_locked = locked; } -void draw_masklay_channel(View2D *v2d, +void draw_masklay_channel(AnimKeylistDrawList *draw_list, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag) { - DLRBT_Tree keys; - bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0; - - BLI_dlrbTree_init(&keys); - - mask_to_keylist(ads, masklay, &keys); - - draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag); - - BLI_dlrbTree_free(&keys); + AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem( + draw_list, ANIM_KEYLIST_MASK_LAYER, ypos, yscale_fac, saction_flag); + draw_elem->ads = ads; + draw_elem->masklay = masklay; + draw_elem->channel_locked = locked; } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index eb91afa5c84..75ce62d5e27 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -92,10 +92,7 @@ void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc) fcu->totvert--; if (fcu->totvert == 0) { - if (fcu->bezt) { - MEM_freeN(fcu->bezt); - } - fcu->bezt = NULL; + MEM_SAFE_FREE(fcu->bezt); } /* recalc handles - only if it won't cause problems */ @@ -136,10 +133,7 @@ bool delete_fcurve_keys(FCurve *fcu) void clear_fcurve_keys(FCurve *fcu) { - if (fcu->bezt) { - MEM_freeN(fcu->bezt); - } - fcu->bezt = NULL; + MEM_SAFE_FREE(fcu->bezt); fcu->totvert = 0; } diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.cc index 47ed2b56300..f6ade11a517 100644 --- a/source/blender/editors/animation/keyframes_keylist.c +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -23,15 +23,16 @@ /* System includes ----------------------------------------------------- */ -#include <float.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> +#include <cfloat> +#include <cmath> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" #include "BLI_dlrbTree.h" #include "BLI_listbase.h" +#include "BLI_range.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -46,16 +47,97 @@ #include "ED_anim_api.h" #include "ED_keyframes_keylist.h" +extern "C" { /* *************************** Keyframe Processing *************************** */ +struct AnimKeylist { + DLRBT_Tree keys; +}; + +static void ED_keylist_init(AnimKeylist *keylist) +{ + BLI_dlrbTree_init(&keylist->keys); +} + +AnimKeylist *ED_keylist_create(void) +{ + AnimKeylist *keylist = static_cast<AnimKeylist *>(MEM_callocN(sizeof(AnimKeylist), __func__)); + ED_keylist_init(keylist); + return keylist; +} + +void ED_keylist_free(AnimKeylist *keylist) +{ + BLI_assert(keylist); + BLI_dlrbTree_free(&keylist->keys); + MEM_freeN(keylist); +} + +const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_exact( + &keylist->keys, compare_ak_cfraPtr, &cfra); +} + +const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +{ + return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); +} + +/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check + * boundary of `max_fra`. */ +const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const Range2f frame_range) +{ + for (const ActKeyColumn *ak = static_cast<const ActKeyColumn *>(keylist->keys.root); ak; + ak = static_cast<const ActKeyColumn *>((ak->cfra < frame_range.min) ? ak->right : + ak->left)) { + if (range2f_in_range(&frame_range, ak->cfra)) { + return ak; + } + } + return nullptr; +} + +bool ED_keylist_is_empty(const struct AnimKeylist *keylist) +{ + return keylist->keys.root == nullptr; +} + +const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) +{ + return (ListBase *)&keylist->keys; +} + +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +{ + BLI_assert(r_frame_range); + + if (ED_keylist_is_empty(keylist)) { + return false; + } + + const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first; + r_frame_range->min = first_column->cfra; + + const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last; + r_frame_range->max = last_column->cfra; + + return true; +} /* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ -BLI_INLINE bool is_cfra_eq(float a, float b) +BLI_INLINE bool is_cfra_eq(const float a, const float b) { return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); } -BLI_INLINE bool is_cfra_lt(float a, float b) +BLI_INLINE bool is_cfra_lt(const float a, const float b) { return (b - a) > BEZT_BINARYSEARCH_THRESH; } @@ -65,8 +147,8 @@ BLI_INLINE bool is_cfra_lt(float a, float b) short compare_ak_cfraPtr(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - const float *cframe = data; - float val = *cframe; + const float *cframe = static_cast<const float *>(data); + const float val = *cframe; if (is_cfra_eq(val, ak->cfra)) { return 0; @@ -81,16 +163,16 @@ short compare_ak_cfraPtr(void *node, void *data) /* --------------- */ /* Set of references to three logically adjacent keys. */ -typedef struct BezTripleChain { +struct BezTripleChain { /* Current keyframe. */ BezTriple *cur; - /* Logical neighbors. May be NULL. */ + /* Logical neighbors. May be nullptr. */ BezTriple *prev, *next; -} BezTripleChain; +}; /* Categorize the interpolation & handle type of the keyframe. */ -static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) +static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt) { if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { return KEYFRAME_HANDLE_AUTO_CLAMP; @@ -110,14 +192,14 @@ static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) /* Determine if the keyframe is an extreme by comparing with neighbors. * Ends of fixed-value sections and of the whole curve are also marked. */ -static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) +static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) { - if (chain->prev == NULL && chain->next == NULL) { + if (chain->prev == nullptr && chain->next == nullptr) { return KEYFRAME_EXTREME_NONE; } /* Keyframe values for the current one and neighbors. */ - float cur_y = chain->cur->vec[1][1]; + const float cur_y = chain->cur->vec[1][1]; float prev_y = cur_y, next_y = cur_y; if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { @@ -138,22 +220,24 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) } /* Bezier handle values for the overshoot check. */ - bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; - bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; - float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; - float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; + const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; + const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; + const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; + const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; /* Detect extremes. One of the neighbors is allowed to be equal to current. */ if (prev_y < cur_y || next_y < cur_y) { - bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); + const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); - return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MAX | + (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0)); } if (prev_y > cur_y || next_y > cur_y) { - bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); + const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); - return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MIN | + (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0)); } return KEYFRAME_EXTREME_NONE; @@ -162,7 +246,7 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) /* Comparator callback used for ActKeyColumns and BezTripleChain */ static short compare_ak_bezt(void *node, void *data) { - BezTripleChain *chain = data; + BezTripleChain *chain = static_cast<BezTripleChain *>(data); return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); } @@ -170,9 +254,10 @@ static short compare_ak_bezt(void *node, void *data) /* New node callback used for building ActKeyColumns from BezTripleChain */ static DLRBT_Node *nalloc_ak_bezt(void *data) { - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; + ActKeyColumn *ak = static_cast<ActKeyColumn *>( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn")); + const BezTripleChain *chain = static_cast<const BezTripleChain *>(data); + const BezTriple *bezt = chain->cur; /* store settings based on state of BezTriple */ ak->cfra = bezt->vec[1][0]; @@ -190,9 +275,9 @@ static DLRBT_Node *nalloc_ak_bezt(void *data) /* Node updater callback used for building ActKeyColumns from BezTripleChain */ static void nupdate_ak_bezt(void *node, void *data) { - ActKeyColumn *ak = node; - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; + ActKeyColumn *ak = static_cast<ActKeyColumn *>(node); + const BezTripleChain *chain = static_cast<const BezTripleChain *>(data); + const BezTriple *bezt = chain->cur; /* set selection status and 'touched' status */ if (BEZT_ISSEL_ANY(bezt)) { @@ -209,10 +294,10 @@ static void nupdate_ak_bezt(void *node, void *data) } /* For interpolation type, select the highest value (enum is sorted). */ - ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); + ak->handle_type = MAX2((eKeyframeHandleDrawOpts)ak->handle_type, bezt_handle_type(bezt)); /* For extremes, detect when combining different states. */ - char new_extreme = bezt_extreme_type(chain); + const char new_extreme = bezt_extreme_type(chain); if (new_extreme != ak->extreme_type) { /* Replace the flat status without adding mixed. */ @@ -230,7 +315,7 @@ static void nupdate_ak_bezt(void *node, void *data) /* Comparator callback used for ActKeyColumns and GPencil frame */ static short compare_ak_gpframe(void *node, void *data) { - bGPDframe *gpf = (bGPDframe *)data; + const bGPDframe *gpf = (bGPDframe *)data; float frame = gpf->framenum; return compare_ak_cfraPtr(node, &frame); @@ -239,8 +324,9 @@ static short compare_ak_gpframe(void *node, void *data) /* New node callback used for building ActKeyColumns from GPencil frames */ static DLRBT_Node *nalloc_ak_gpframe(void *data) { - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - bGPDframe *gpf = (bGPDframe *)data; + ActKeyColumn *ak = static_cast<ActKeyColumn *>( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); + const bGPDframe *gpf = (bGPDframe *)data; /* store settings based on state of BezTriple */ ak->cfra = gpf->framenum; @@ -261,7 +347,7 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data) static void nupdate_ak_gpframe(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - bGPDframe *gpf = (bGPDframe *)data; + const bGPDframe *gpf = (bGPDframe *)data; /* set selection status and 'touched' status */ if (gpf->flag & GP_FRAME_SELECT) { @@ -283,7 +369,7 @@ static void nupdate_ak_gpframe(void *node, void *data) /* Comparator callback used for ActKeyColumns and GPencil frame */ static short compare_ak_masklayshape(void *node, void *data) { - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; float frame = masklay_shape->frame; return compare_ak_cfraPtr(node, &frame); @@ -292,8 +378,9 @@ static short compare_ak_masklayshape(void *node, void *data) /* New node callback used for building ActKeyColumns from GPencil frames */ static DLRBT_Node *nalloc_ak_masklayshape(void *data) { - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + ActKeyColumn *ak = static_cast<ActKeyColumn *>( + MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; /* store settings based on state of BezTriple */ ak->cfra = masklay_shape->frame; @@ -309,7 +396,7 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data) static void nupdate_ak_masklayshape(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; /* set selection status and 'touched' status */ if (masklay_shape->flag & MASK_SHAPE_SELECT) { @@ -323,33 +410,33 @@ static void nupdate_ak_masklayshape(void *node, void *data) /* --------------- */ /* Add the given BezTriple to the given 'list' of Keyframes */ -static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt) +static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) { - if (ELEM(NULL, keys, bezt)) { + if (ELEM(nullptr, keylist, bezt)) { return; } - BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); + BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); } /* Add the given GPencil Frame to the given 'list' of Keyframes */ -static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf) +static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) { - if (ELEM(NULL, keys, gpf)) { + if (ELEM(nullptr, keylist, gpf)) { return; } - BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); + BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); } /* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ -static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape) +static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape) { - if (ELEM(NULL, keys, masklay_shape)) { + if (ELEM(nullptr, keylist, masklay_shape)) { return; } - BLI_dlrbTree_add(keys, + BLI_dlrbTree_add(&keylist->keys, compare_ak_masklayshape, nalloc_ak_masklayshape, nupdate_ak_masklayshape, @@ -360,7 +447,9 @@ static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *mas static const ActKeyBlockInfo dummy_keyblock = {0}; -static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn) +static void compute_keyblock_data(ActKeyBlockInfo *info, + const BezTriple *prev, + const BezTriple *beztn) { memset(info, 0, sizeof(ActKeyBlockInfo)); @@ -423,34 +512,34 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) } } -static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { - ActKeyColumn *col = keys->first; + ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->keys.first); if (bezt && bezt_len >= 2) { ActKeyBlockInfo block; /* Find the first key column while inserting dummy blocks. */ - for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { + for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { add_keyblock_info(col, &dummy_keyblock); } - BLI_assert(col != NULL); + BLI_assert(col != nullptr); /* Insert real blocks. */ - for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) { + for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) { /* Wrong order of bezier keys: resync position. */ if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); - if (newcol != NULL) { + if (newcol != nullptr) { col = newcol; /* The previous keyblock is garbage too. */ - if (col->prev != NULL) { + if (col->prev != nullptr) { add_keyblock_info(col->prev, &dummy_keyblock); } } @@ -467,16 +556,16 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be compute_keyblock_data(&block, bezt, bezt + 1); - for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { + for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { add_keyblock_info(col, &block); } - BLI_assert(col != NULL); + BLI_assert(col != nullptr); } } /* Insert dummy blocks at the end. */ - for (; col != NULL; col = col->next) { + for (; col != nullptr; col = col->next) { add_keyblock_info(col, &dummy_keyblock); } } @@ -486,28 +575,28 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be * This must be called even by animation sources that don't generate * keyblocks to keep the data structure consistent after adding columns. */ -static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(keys); + BLI_dlrbTree_linkedlist_sync(&keylist->keys); /* Find the curve count */ int max_curve = 0; - LISTBASE_FOREACH (ActKeyColumn *, col, keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { max_curve = MAX2(max_curve, col->totcurve); } /* Propagate blocks to inserted keys */ - ActKeyColumn *prev_ready = NULL; + ActKeyColumn *prev_ready = nullptr; - LISTBASE_FOREACH (ActKeyColumn *, col, keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { /* Pre-existing column. */ if (col->totcurve > 0) { prev_ready = col; } /* Newly inserted column, so copy block data from previous. */ - else if (prev_ready != NULL) { + else if (prev_ready != nullptr) { col->totblock = prev_ready->totblock; memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo)); } @@ -516,18 +605,18 @@ static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) } /* Add blocks on top */ - add_bezt_to_keyblocks_list(keys, bezt, bezt_len); + add_bezt_to_keyblocks_list(keylist, bezt, bezt_len); } /* --------- */ -bool actkeyblock_is_valid(ActKeyColumn *ac) +bool actkeyblock_is_valid(const ActKeyColumn *ac) { - return ac != NULL && ac->next != NULL && ac->totblock > 0; + return ac != nullptr && ac->next != nullptr && ac->totblock > 0; } /* Checks if ActKeyBlock should exist... */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac) +int actkeyblock_get_valid_hold(const ActKeyColumn *ac) { /* check that block is valid */ if (!actkeyblock_is_valid(ac)) { @@ -540,34 +629,32 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac) /* *************************** Keyframe List Conversions *************************** */ -void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) +void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag) { if (ac) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; + ListBase anim_data = {nullptr, nullptr}; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; + ANIM_animdata_filter( + ac, &anim_data, filter, ac->data, static_cast<eAnimCont_Types>(ac->datatype)); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { /* Why not use all #eAnim_KeyType here? * All of the other key types are actually "summaries" themselves, * and will just end up duplicating stuff that comes up through * standard filtering of just F-Curves. Given the way that these work, * there isn't really any benefit at all from including them. - Aligorith */ - switch (ale->datatype) { case ALE_FCURVE: - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag); break; case ALE_MASKLAY: - mask_to_keylist(ac->ads, ale->data, keys); + mask_to_keylist(ac->ads, static_cast<MaskLayer *>(ale->data), keylist); break; case ALE_GPFRAME: - gpl_to_keylist(ac->ads, ale->data, keys); + gpl_to_keylist(ac->ads, static_cast<bGPDlayer *>(ale->data), keylist); break; default: // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); @@ -579,16 +666,14 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) } } -void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag) +void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag) { - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; + bAnimContext ac = {nullptr}; + ListBase anim_data = {nullptr, nullptr}; - bAnimListElem dummychan = {NULL}; + bAnimListElem dummychan = {nullptr}; - if (sce == NULL) { + if (sce == nullptr) { return; } @@ -603,28 +688,27 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction ac.datatype = ANIMCONT_CHANNEL; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype)); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag) +void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag) { - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; + bAnimContext ac = {nullptr}; + ListBase anim_data = {nullptr, nullptr}; - bAnimListElem dummychan = {NULL}; - Base dummybase = {NULL}; + bAnimListElem dummychan = {nullptr}; + Base dummybase = {nullptr}; - if (ob == NULL) { + if (ob == nullptr) { return; } @@ -641,12 +725,13 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl ac.datatype = ANIMCONT_CHANNEL; /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype)); /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); @@ -654,44 +739,45 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, - DLRBT_Tree *keys, - int saction_flag) + AnimKeylist *keylist, + const int saction_flag) { - if (cache_file == NULL) { + if (cache_file == nullptr) { return; } /* create a dummy wrapper data to work with */ - bAnimListElem dummychan = {NULL}; + bAnimListElem dummychan = {nullptr}; dummychan.type = ANIMTYPE_DSCACHEFILE; dummychan.data = cache_file; dummychan.id = &cache_file->id; dummychan.adt = cache_file->adt; - bAnimContext ac = {NULL}; + bAnimContext ac = {nullptr}; ac.ads = ads; ac.data = &dummychan; ac.datatype = ANIMCONT_CHANNEL; /* get F-Curves to take keyframes from */ - ListBase anim_data = {NULL, NULL}; - int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + ListBase anim_data = {nullptr, nullptr}; + const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter( + &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype)); /* loop through each F-Curve, grabbing the keyframes */ - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag); } ANIM_animdata_freelist(&anim_data); } -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag) +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) { if (fcu && fcu->totvert && fcu->bezt) { /* apply NLA-mapping (if applicable) */ if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false); } /* Check if the curve is cyclic. */ @@ -699,95 +785,95 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; /* loop through beztriples, making ActKeysColumns */ - BezTripleChain chain = {0}; + BezTripleChain chain = {nullptr}; for (int v = 0; v < fcu->totvert; v++) { chain.cur = &fcu->bezt[v]; /* Neighbor keys, accounting for being cyclic. */ if (do_extremes) { - chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL; - chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; + chain.prev = (v > 0) ? &fcu->bezt[v - 1] : + is_cyclic ? &fcu->bezt[fcu->totvert - 2] : + nullptr; + chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : + is_cyclic ? &fcu->bezt[1] : + nullptr; } - add_bezt_to_keycolumns_list(keys, &chain); + add_bezt_to_keycolumns_list(keylist, &chain); } /* Update keyblocks. */ - update_keyblocks(keys, fcu->bezt, fcu->totvert); + update_keyblocks(keylist, fcu->bezt, fcu->totvert); /* unapply NLA-mapping if applicable */ if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); + ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false); } } } -void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag) +void agroup_to_keylist(AnimData *adt, + bActionGroup *agrp, + AnimKeylist *keylist, + const int saction_flag) { - FCurve *fcu; - if (agrp) { /* loop through F-Curves */ - for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys, saction_flag); + LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) { + if (fcu->grp != agrp) { + break; + } + fcurve_to_keylist(adt, fcu, keylist, saction_flag); } } } -void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag) +void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag) { - FCurve *fcu; - if (act) { /* loop through F-Curves */ - for (fcu = act->curves.first; fcu; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys, saction_flag); + LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { + fcurve_to_keylist(adt, fcu, keylist, saction_flag); } } } -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active) { - bGPDlayer *gpl; - - if (gpd && keys) { + if (gpd && keylist) { /* for now, just aggregate out all the frames, but only for visible layers */ - for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) { + LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { - gpl_to_keylist(ads, gpl, keys); + gpl_to_keylist(ads, gpl, keylist); } } } } } -void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys) +void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) { - bGPDframe *gpf; - - if (gpl && keys) { + if (gpl && keylist) { /* Although the frames should already be in an ordered list, * they are not suitable for displaying yet. */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - add_gpframe_to_keycolumns_list(keys, gpf); + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + add_gpframe_to_keycolumns_list(keylist, gpf); } - update_keyblocks(keys, NULL, 0); + update_keyblocks(keylist, nullptr, 0); } } -void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys) +void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) { - MaskLayerShape *masklay_shape; - - if (masklay && keys) { - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - add_masklay_to_keycolumns_list(keys, masklay_shape); + if (masklay && keylist) { + LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { + add_masklay_to_keycolumns_list(keylist, masklay_shape); } - update_keyblocks(keys, NULL, 0); + update_keyblocks(keylist, nullptr, 0); } } +} diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 6af033f3cf2..8aeb6a57124 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -244,6 +244,10 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDope UI_block_align_end(block); UI_block_layout_resolve(block, NULL, NULL); + /* Make sure the events are consumed from the search and don't reach other UI blocks since this + * is drawn on top of animation-channels. */ + UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS); + UI_block_bounds_set_normal(block, 0); UI_block_end(C, block); UI_block_draw(C, block); diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index e362a2e2f40..990e7589d9d 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -307,10 +307,7 @@ static void laplacian_system_construct_end(LaplacianSystem *sys) MEM_freeN(sys->faces); sys->faces = NULL; - if (sys->varea) { - MEM_freeN(sys->varea); - sys->varea = NULL; - } + MEM_SAFE_FREE(sys->varea); BLI_edgehash_free(sys->edgehash, NULL); sys->edgehash = NULL; diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index cb70b2810d1..646356e7a45 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -304,8 +304,6 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) { Object *ob = get_poselib_object(C); bAction *act = (ob) ? ob->poselib : NULL; - DLRBT_Tree keys; - ActKeyColumn *ak; TimeMarker *marker, *markern; /* validate action */ @@ -315,11 +313,11 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) } /* determine which frames have keys */ - BLI_dlrbTree_init(&keys); - action_to_keylist(NULL, act, &keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + action_to_keylist(NULL, act, keylist, 0); /* for each key, make sure there is a corresponding pose */ - for (ak = keys.first; ak; ak = ak->next) { + LISTBASE_FOREACH (const ActKeyColumn *, ak, ED_keylist_listbase(keylist)) { /* check if any pose matches this */ /* TODO: don't go looking through the list like this every time... */ for (marker = act->markers.first; marker; marker = marker->next) { @@ -356,7 +354,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) } /* free temp memory */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* send notifiers for this - using keyframe editing notifiers, since action * may be being shown in anim editors as active action diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 32440f941ba..91a5dc67a21 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -299,16 +299,16 @@ static void poselib_tempload_exit(PoseBlendData *pbd) static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op) { bool asset_handle_valid; - const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C); const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); /* Poll callback should check. */ - BLI_assert((asset_library != NULL) && asset_handle_valid); + BLI_assert((asset_library_ref != NULL) && asset_handle_valid); PoseBlendData *pbd = op->customdata; pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle); return (bAction *)ED_asset_temp_id_consumer_ensure_local_id( - pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports); + pbd->temp_id_consumer, C, asset_library_ref, ID_AC, CTX_data_main(C), op->reports); } static bAction *flip_pose(bContext *C, Object *ob, bAction *action) @@ -423,7 +423,7 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op) case POSE_BLEND_ORIGINAL: /* Cleanup should not be called directly from these states. */ BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state"); - BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator"); + BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator"); ATTR_FALLTHROUGH; case POSE_BLEND_CANCEL: ED_pose_backup_restore(pbd->pose_backup); @@ -540,10 +540,10 @@ static bool poselib_asset_in_context(bContext *C) { bool asset_handle_valid; /* Check whether the context provides the asset data needed to add a pose. */ - const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C); AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); - return (asset_library != NULL) && asset_handle_valid && + return (asset_library_ref != NULL) && asset_handle_valid && (ED_asset_handle_get_id_type(&asset_handle) == ID_AC); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 38f562ebf25..bc5cbd92deb 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -146,7 +146,7 @@ typedef struct tPoseSlideOp { /** links between posechannels and f-curves for all the pose objects. */ ListBase pfLinks; /** binary tree for quicker searching for keyframes (when applicable) */ - DLRBT_Tree keys; + struct AnimKeylist *keylist; /** current frame number - global time */ int cframe; @@ -277,7 +277,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up * to the caller of this (usually only invoke() will do it, to make things more efficient). */ - BLI_dlrbTree_init(&pso->keys); + pso->keylist = ED_keylist_create(); /* Initialize numeric input. */ initNumInput(&pso->num); @@ -306,7 +306,7 @@ static void pose_slide_exit(bContext *C, wmOperator *op) poseAnim_mapping_free(&pso->pfLinks); /* Free RB-BST for keyframes (if it contained data). */ - BLI_dlrbTree_free(&pso->keys); + ED_keylist_free(pso->keylist); if (pso->ob_data_array != NULL) { MEM_freeN(pso->ob_data_array); @@ -971,60 +971,56 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * /* Do this for each F-Curve. */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0); + fcurve_to_keylist(pfl->ob->adt, fcu, pso->keylist, 0); } } /* Cancel if no keyframes found. */ - if (pso->keys.root) { - ActKeyColumn *ak; - float cframe = (float)pso->cframe; - - /* Firstly, check if the current frame is a keyframe. */ - ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe); - - if (ak == NULL) { - /* Current frame is not a keyframe, so search. */ - ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev( - &pso->keys, compare_ak_cfraPtr, &cframe); - ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next( - &pso->keys, compare_ak_cfraPtr, &cframe); - - /* New set the frames. */ - /* Prev frame. */ - pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* Next frame. */ - pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - else { - /* Current frame itself is a keyframe, so just take keyframes on either side. */ - /* Prev frame. */ - pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); - RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); - /* Next frame. */ - pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); - RNA_int_set(op->ptr, "next_frame", pso->nextFrame); - } - - /* Apply NLA mapping corrections so the frame look-ups work. */ - for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { - tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; - if (ob_data->valid) { - ob_data->prevFrameF = BKE_nla_tweakedit_remap( - ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); - ob_data->nextFrameF = BKE_nla_tweakedit_remap( - ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); - } - } - } - else { + if (ED_keylist_is_empty(pso->keylist)) { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); pose_slide_exit(C, op); return OPERATOR_CANCELLED; } + float cframe = (float)pso->cframe; + + /* Firstly, check if the current frame is a keyframe. */ + const ActKeyColumn *ak = ED_keylist_find_exact(pso->keylist, cframe); + + if (ak == NULL) { + /* Current frame is not a keyframe, so search. */ + const ActKeyColumn *pk = ED_keylist_find_prev(pso->keylist, cframe); + const ActKeyColumn *nk = ED_keylist_find_next(pso->keylist, cframe); + + /* New set the frames. */ + /* Prev frame. */ + pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* Next frame. */ + pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + else { + /* Current frame itself is a keyframe, so just take keyframes on either side. */ + /* Prev frame. */ + pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); + RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); + /* Next frame. */ + pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1); + RNA_int_set(op->ptr, "next_frame", pso->nextFrame); + } + + /* Apply NLA mapping corrections so the frame look-ups work. */ + for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) { + tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index]; + if (ob_data->valid) { + ob_data->prevFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP); + ob_data->nextFrameF = BKE_nla_tweakedit_remap( + ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP); + } + } + /* Initial apply for operator. */ /* TODO: need to calculate factor for initial round too. */ if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) { @@ -1705,26 +1701,22 @@ typedef union tPosePropagate_ModeData { */ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float startFrame) { - DLRBT_Tree keys; + struct AnimKeylist *keylist = ED_keylist_create(); Object *ob = pfl->ob; AnimData *adt = ob->adt; LinkData *ld; float endFrame = startFrame; - /* Set up optimized data-structures for searching for relevant keyframes + holds. */ - BLI_dlrbTree_init(&keys); - for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; - fcurve_to_keylist(adt, fcu, &keys, 0); + fcurve_to_keylist(adt, fcu, keylist, 0); } /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value * - the best case would be one that starts on the frame itself */ - ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact( - &keys, compare_ak_cfraPtr, &startFrame); + const ActKeyColumn *ab = ED_keylist_find_exact(keylist, startFrame); /* There are only two cases for no-exact match: * 1) the current frame is just before another key but not on a key itself @@ -1735,11 +1727,11 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st */ if (ab == NULL) { /* We've got case 1, so try the one after. */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame); + ab = ED_keylist_find_next(keylist, startFrame); if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { /* Try the block before this frame then as last resort. */ - ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame); + ab = ED_keylist_find_prev(keylist, startFrame); } } @@ -1754,7 +1746,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st if (ab) { /* Go to next if it is also valid and meets "extension" criteria. */ while (ab->next) { - ActKeyColumn *abn = ab->next; + const ActKeyColumn *abn = ab->next; /* Must be valid. */ if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) { @@ -1774,7 +1766,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st } /* Free temp memory. */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* Return the end frame we've found. */ return endFrame; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 1118e84ef4f..3798ca308ed 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -908,7 +908,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); /* Recalculate paths if any of the bones have paths... */ - if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL); } @@ -1219,7 +1219,7 @@ static int pose_clear_transform_generic_exec(bContext *C, ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); /* now recalculate paths */ - if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { + if (ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) { ED_pose_recalculate_paths(C, scene, ob_iter, POSE_PATH_CALC_RANGE_FULL); } diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index c51ce422c25..efb99410d3d 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -36,7 +36,7 @@ struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset); ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset); int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset); void ED_asset_handle_get_full_library_path(const struct bContext *C, - const struct AssetLibraryReference *asset_library, + const struct AssetLibraryReference *asset_library_ref, const struct AssetHandle *asset, char r_full_lib_path[]); diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h index 9af08c5c52b..7c10d88262e 100644 --- a/source/blender/editors/asset/ED_asset_temp_id_consumer.h +++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h @@ -39,7 +39,7 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); struct ID *ED_asset_temp_id_consumer_ensure_local_id( AssetTempIDConsumer *consumer, const struct bContext *C, - const struct AssetLibraryReference *asset_library, + const struct AssetLibraryReference *asset_library_ref, ID_Type id_type, struct Main *bmain, struct ReportList *reports); diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index aae85e61372..5c8d0b1349c 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -60,13 +60,13 @@ int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset) } void ED_asset_handle_get_full_library_path(const bContext *C, - const AssetLibraryReference *asset_library, + const AssetLibraryReference *asset_library_ref, const AssetHandle *asset, char r_full_lib_path[FILE_MAX_LIBEXTRA]) { *r_full_lib_path = '\0'; - std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); + std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library_ref, *asset); if (asset_path.empty()) { return; } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index f18cf9712db..d69a2cae94d 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -36,7 +36,7 @@ using PointerRNAVec = blender::Vector<PointerRNA>; static bool asset_operation_poll(bContext * /*C*/) { - return U.experimental.use_asset_browser; + return U.experimental.use_extended_asset_browser; } /** @@ -110,7 +110,7 @@ void AssetMarkHelper::reportResults(ReportList &reports) const { /* User feedback on failure. */ if (!wasSuccessful()) { - if ((stats.tot_already_asset > 0)) { + if (stats.tot_already_asset > 0) { BKE_report(&reports, RPT_ERROR, "Selected data-blocks are already assets (or do not support use as assets)"); @@ -266,7 +266,7 @@ static void ASSET_OT_clear(wmOperatorType *ot) static bool asset_list_refresh_poll(bContext *C) { - const AssetLibraryReference *library = CTX_wm_asset_library(C); + const AssetLibraryReference *library = CTX_wm_asset_library_ref(C); if (!library) { return false; } @@ -276,7 +276,7 @@ static bool asset_list_refresh_poll(bContext *C) static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) { - const AssetLibraryReference *library = CTX_wm_asset_library(C); + const AssetLibraryReference *library = CTX_wm_asset_library_ref(C); ED_assetlist_clear(library, C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index bed35fdeeb5..f664eab5cbb 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -60,14 +60,14 @@ class AssetTemporaryIDConsumer : NonCopyable, NonMovable { } ID *import_id(const bContext *C, - const AssetLibraryReference &asset_library, + const AssetLibraryReference &asset_library_ref, ID_Type id_type, Main &bmain, ReportList &reports) { const char *asset_name = ED_asset_handle_get_name(&handle_); char blend_file_path[FILE_MAX_LIBEXTRA]; - ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); + ED_asset_handle_get_full_library_path(C, &asset_library_ref, &handle_, blend_file_path); temp_lib_context_ = BLO_library_temp_load_id( &bmain, blend_file_path, id_type, asset_name, &reports); @@ -99,12 +99,12 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, const bContext *C, - const AssetLibraryReference *asset_library, + const AssetLibraryReference *asset_library_ref, ID_Type id_type, Main *bmain, ReportList *reports) { - if (!(consumer_ && asset_library && bmain && reports)) { + if (!(consumer_ && asset_library_ref && bmain && reports)) { return nullptr; } AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_); @@ -112,5 +112,5 @@ ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, if (ID *local_id = consumer->get_local_id()) { return local_id; } - return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); + return consumer->import_id(C, *asset_library_ref, id_type, *bmain, *reports); } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index e7d97ce343c..c399abfa52d 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1848,10 +1848,7 @@ static void ed_surf_delete_selected(Object *obedit) nu->pntsv = 1; SWAP(short, nu->orderu, nu->orderv); BKE_nurb_order_clamp_u(nu); - if (nu->knotsv) { - MEM_freeN(nu->knotsv); - } - nu->knotsv = NULL; + MEM_SAFE_FREE(nu->knotsv); } else { nu->pntsu = newu; @@ -4650,10 +4647,7 @@ static int make_segment_exec(bContext *C, wmOperator *op) /* now join the knots */ if (nu1->type == CU_NURBS) { - if (nu1->knotsu != NULL) { - MEM_freeN(nu1->knotsu); - nu1->knotsu = NULL; - } + MEM_SAFE_FREE(nu1->knotsu); BKE_nurb_knot_calc_u(nu1); } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 2be55accd3a..d1fe162fc4a 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -431,7 +431,7 @@ Nurb *ED_curve_add_nurbs_primitive( if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) { ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent); } - else if ((U.flag & USER_ADD_VIEWALIGNED)) { + else if (U.flag & USER_ADD_VIEWALIGNED) { ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]); } else { @@ -466,7 +466,7 @@ Nurb *ED_curve_add_nurbs_primitive( if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) { ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent); } - else if ((U.flag & USER_ADD_VIEWALIGNED)) { + else if (U.flag & USER_ADD_VIEWALIGNED) { ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]); } else { diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index e43e4194c51..39fb2882e4b 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -439,37 +439,27 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); } -static int kill_selection(Object *obedit, int ins) /* 1 == new character */ +static int kill_selection(Object *obedit, int ins) /* ins == new character len */ { Curve *cu = obedit->data; EditFont *ef = cu->editfont; int selend, selstart, direction; - int offset = 0; int getfrom; direction = BKE_vfont_select_get(obedit, &selstart, &selend); if (direction) { int size; - if (ins) { - offset = 1; - } if (ef->pos >= selstart) { - ef->pos = selstart + offset; + ef->pos = selstart + ins; } if ((direction == -1) && ins) { - selstart++; - selend++; - } - getfrom = selend + offset; - if (ins == 0) { - getfrom++; - } - size = (ef->len * sizeof(*ef->textbuf)) - (selstart * sizeof(*ef->textbuf)) + - (offset * sizeof(*ef->textbuf)); - memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size); - memmove(ef->textbufinfo + selstart, - ef->textbufinfo + getfrom, - ((ef->len - selstart) + offset) * sizeof(CharInfo)); + selstart += ins; + selend += ins; + } + getfrom = selend + 1; + size = ef->len - selend; /* This is equivalent to: `(ef->len - getfrom) + 1(null)`. */ + memmove(ef->textbuf + selstart, ef->textbuf + getfrom, sizeof(*ef->textbuf) * size); + memmove(ef->textbufinfo + selstart, ef->textbufinfo + getfrom, sizeof(CharInfo) * size); ef->len -= ((selend - selstart) + 1); ef->selstart = ef->selend = 0; } @@ -1650,7 +1640,7 @@ static int insert_text_exec(bContext *C, wmOperator *op) MEM_freeN(inserted_text); MEM_freeN(inserted_utf8); - kill_selection(obedit, 1); + kill_selection(obedit, len); text_update_edited(C, obedit, FO_EDIT); return OPERATOR_FINISHED; diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index beb22d43930..c4916b9182f 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -583,6 +583,9 @@ set(ICON_NAMES uv_facesel uv_islandsel uv_sync_select + gp_caps_flat + gp_caps_round + fixed_size transform_origins gizmo orientation_cursor diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 9bf44370c80..bf47704746b 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -836,10 +836,6 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p) /* exit with error if no valid points from this stroke */ if (totelem == 0) { - if (G.debug & G_DEBUG) { - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", - gpd->runtime.sbuffer_used); - } return; } @@ -1263,9 +1259,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No active view for painting\n"); - } return 0; } @@ -1294,11 +1287,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) if (region->regiondata == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf( - "Error: 3D-View active region doesn't have any region data, so cannot be " - "drawable\n"); - } return 0; } break; @@ -1325,9 +1313,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* check that gpencil data is allowed to be drawn */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); - } return 0; } break; @@ -1387,9 +1372,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) /* unsupported views */ default: { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Annotations are not supported in this editor\n"); - } return 0; } } @@ -1398,9 +1380,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) gpd_ptr = ED_annotation_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Current context doesn't allow for any Annotation data\n"); - } return 0; } @@ -1507,7 +1486,6 @@ static void annotation_session_cleanup(tGPsdata *p) /* free stroke buffer */ if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - free sbuffer\n"); */ MEM_freeN(gpd->runtime.sbuffer); gpd->runtime.sbuffer = NULL; } @@ -1545,9 +1523,6 @@ static void annotation_paint_initstroke(tGPsdata *p, } if (p->gpl->flag & GP_LAYER_LOCKED) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Cannot paint on locked layer\n"); - } return; } @@ -1573,7 +1548,6 @@ static void annotation_paint_initstroke(tGPsdata *p, if (has_layer_to_erase == false) { p->status = GP_STATUS_CAPTURE; - // if (G.debug & G_DEBUG) printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); return; } @@ -1593,9 +1567,6 @@ static void annotation_paint_initstroke(tGPsdata *p, if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No frame created (gpencil_paint_init)\n"); - } return; } @@ -2063,9 +2034,6 @@ static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgr BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - } return; } @@ -2221,29 +2189,22 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) tGPsdata *p = NULL; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - /* printf("GPencil - Starting Re-Drawing\n"); */ - /* try to initialize context data needed while drawing */ if (!annotation_draw_init(C, op, NULL)) { if (op->customdata) { MEM_freeN(op->customdata); } - /* printf("\tGP - no valid data\n"); */ return OPERATOR_CANCELLED; } p = op->customdata; - /* printf("\tGP - Start redrawing stroke\n"); */ - /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), * setting the relevant values in context at each step, then applying */ RNA_BEGIN (op->ptr, itemptr, "stroke") { float mousef[2]; - /* printf("\t\tGP - stroke elem\n"); */ - /* get relevant data for this point from stroke */ RNA_float_get_array(&itemptr, "mouse", mousef); p->mval[0] = (int)mousef[0]; @@ -2277,8 +2238,6 @@ static int annotation_draw_exec(bContext *C, wmOperator *op) } RNA_END; - /* printf("\tGP - done\n"); */ - /* cleanup */ annotation_draw_exit(C, op); @@ -2301,18 +2260,11 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); } - if (G.debug & G_DEBUG) { - printf("GPencil - Starting Drawing\n"); - } - /* try to initialize context data needed while drawing */ if (!annotation_draw_init(C, op, event)) { if (op->customdata) { MEM_freeN(op->customdata); } - if (G.debug & G_DEBUG) { - printf("\tGP - no valid data\n"); - } return OPERATOR_CANCELLED; } @@ -2361,7 +2313,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ - /* printf("\tGP - set first spot\n"); */ p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ @@ -2370,7 +2321,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev } else { /* toolbar invoked - don't start drawing yet... */ - /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ op->flag |= OP_IS_MODAL_CURSOR_REGION; } @@ -2399,8 +2349,6 @@ static tGPsdata *annotation_stroke_begin(bContext *C, wmOperator *op) p->status = GP_STATUS_ERROR; } - /* printf("\t\tGP - start stroke\n"); */ - /* we may need to set up paint env again if we're resuming */ /* XXX: watch it with the paintmode! in future, * it'd be nice to allow changing paint-mode when in sketching-sessions */ @@ -2537,8 +2485,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } } - // printf("\tGP - handle modal event...\n"); - /* Exit painting mode (and/or end current stroke) * * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) @@ -2547,7 +2493,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (event->val == KM_PRESS && ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) { /* exit() ends the current stroke before cleaning up */ - /* printf("\t\tGP - end of paint op + end of stroke\n"); */ p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2571,7 +2516,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if (sketch) { /* end stroke only, and then wait to resume painting soon */ - /* printf("\t\tGP - end stroke only\n"); */ annotation_stroke_end(op); /* If eraser mode is on, turn it off after the stroke finishes @@ -2602,7 +2546,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } else { - /* printf("\t\tGP - end of stroke + op\n"); */ p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2619,18 +2562,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve ARegion *current_region = BKE_area_find_region_xy( p->area, RGN_TYPE_ANY, event->x, event->y); - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n", - current_region, - p->region, - event->x, - event->y, - p->area->totrct.xmin, - p->area->totrct.ymin, - p->area->totrct.xmax, - p->area->totrct.ymax); - } - if (current_region) { /* Assume that since we found the cursor in here, it is in bounds * and that this should be the region that we begin drawing in @@ -2642,10 +2573,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } } } else if (p->region) { @@ -2657,10 +2584,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: No active region found in GP Paint session data\n", __func__); - } } if (in_bounds) { @@ -2719,7 +2642,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve } else { /* event handled, so just tag as running modal */ - /* printf("\t\t\t\tGP - add point handled!\n"); */ estate = OPERATOR_RUNNING_MODAL; } } @@ -2729,7 +2651,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve /* just resize the brush (local version) * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys */ - /* printf("\t\tGP - resize eraser\n"); */ switch (event->type) { case WHEELDOWNMOUSE: /* larger */ case EVT_PADPLUSKEY: @@ -2787,12 +2708,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: /* event doesn't need to be handled */ -#if 0 - printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", - event->type, - event->type == MIDDLEMOUSE, - event->type == MOUSEMOVE); -#endif break; } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ee3536c2f3f..406a7ac77fc 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -389,9 +389,6 @@ static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, *r_tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; gtd->tot_time += *r_tot_gaps_time; - if (G.debug & G_DEBUG) { - printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *r_tot_gaps_time, *nbr_gaps); - } if (gtd->gap_randomness > 0.0f) { BLI_rng_srandom(rng, gtd->seed); } @@ -464,9 +461,6 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports, INSERTKEY_FAST); last_valid_time = cfra; } - else if (G.debug & G_DEBUG) { - printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); - } } else if (i == end_stroke_idx) { /* Always try to insert end point of a curve (should be safe enough, anyway...) */ @@ -546,13 +540,6 @@ static void gpencil_stroke_path_animation(bContext *C, act = ED_id_action_ensure(bmain, (ID *)cu); fcu = ED_action_fcurve_ensure(bmain, act, NULL, &ptr, "eval_time", 0); - if (G.debug & G_DEBUG) { - printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (int i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - } - if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { float cfra; @@ -610,10 +597,6 @@ static void gpencil_stroke_path_animation(bContext *C, time_range = (float)(gtd->end_frame - gtd->start_frame); } - if (G.debug & G_DEBUG) { - printf("GP Stroke Path Conversion: Starting keying!\n"); - } - gpencil_stroke_path_animation_add_keyframes( reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time); @@ -623,14 +606,6 @@ static void gpencil_stroke_path_animation(bContext *C, /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ calchandles_fcurve(fcu); - if (G.debug & G_DEBUG) { - printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (int i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - printf("\n\n"); - } - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); /* send updates */ @@ -1588,14 +1563,8 @@ static int gpencil_convert_layer_exec(bContext *C, wmOperator *op) C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); /* free temp memory */ - if (gtd.dists) { - MEM_freeN(gtd.dists); - gtd.dists = NULL; - } - if (gtd.times) { - MEM_freeN(gtd.times); - gtd.times = NULL; - } + MEM_SAFE_FREE(gtd.dists); + MEM_SAFE_FREE(gtd.times); /* notifiers */ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 67e1bd5294b..0c88d678ef4 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -646,7 +646,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) tgpf->sizey = (int)tgpf->region->winy; char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, true, false, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create( + tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, err_out); if (offscreen == NULL) { printf("GPencil - Fill - Unable to create fill buffer\n"); return false; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index d1a1e417d9e..b6730cb123b 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -375,6 +375,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot); void GPENCIL_OT_select_first(struct wmOperatorType *ot); void GPENCIL_OT_select_last(struct wmOperatorType *ot); void GPENCIL_OT_select_alternate(struct wmOperatorType *ot); +void GPENCIL_OT_select_random(struct wmOperatorType *ot); void GPENCIL_OT_select_vertex_color(struct wmOperatorType *ot); void GPENCIL_OT_duplicate(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 8640ffa67cf..a8bd3b11bb1 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -890,9 +890,9 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent } case MOUSEMOVE: /* calculate new position */ { - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update shift based on position of mouse */ + /* Update shift based on position of mouse. */ gpencil_mouse_update_shift(tgpi, op, event); /* update screen */ diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 1882285a230..0939d53736b 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -316,7 +316,8 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) ob_eval->obmat, frame_offset, use_seams, - use_faces); + use_faces, + true); /* Reproject all un-tagged created strokes. */ if (project_type != GP_REPROJECT_KEEP) { diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 35640cf3b66..8c78a402e81 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -555,6 +555,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_first); WM_operatortype_append(GPENCIL_OT_select_last); WM_operatortype_append(GPENCIL_OT_select_alternate); + WM_operatortype_append(GPENCIL_OT_select_random); WM_operatortype_append(GPENCIL_OT_select_vertex_color); WM_operatortype_append(GPENCIL_OT_duplicate); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index d6f6dbb2b10..d2dbf6ab2a6 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -976,10 +976,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* exit with error if no valid points from this stroke */ if (totelem == 0) { - if (G.debug & G_DEBUG) { - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", - gpd->runtime.sbuffer_used); - } return; } @@ -996,6 +992,9 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) gps->inittime = p->inittime; gps->uv_scale = 1.0f; + /* Set stroke caps. */ + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; + /* allocate enough memory for a continuous array for storage points */ const int subdivide = brush->gpencil_settings->draw_subdivide; @@ -1946,9 +1945,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No active view for painting\n"); - } return 0; } @@ -1977,11 +1973,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) if (region->regiondata == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf( - "Error: 3D-View active region doesn't have any region data, so cannot be " - "drawable\n"); - } return 0; } @@ -2007,9 +1998,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) /* unsupported views */ default: { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Active view not appropriate for Grease Pencil drawing\n"); - } return 0; } } @@ -2018,9 +2006,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Current context doesn't allow for any Grease Pencil data\n"); - } return 0; } @@ -2144,9 +2129,6 @@ static void gpencil_paint_initstroke(tGPsdata *p, if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Cannot paint on locked layer\n"); - } return; } @@ -2225,9 +2207,6 @@ static void gpencil_paint_initstroke(tGPsdata *p, if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: No frame created (gpencil_paint_init)\n"); - } if (!IS_AUTOKEY_ON(scene)) { BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke"); } @@ -2821,9 +2800,6 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) { - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - } return; } @@ -3195,10 +3171,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - if (G.debug & G_DEBUG) { - printf("GPencil - Starting Drawing\n"); - } - /* support for tablets eraser pen */ if (gpencil_is_tablet_eraser_active(event)) { RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); @@ -3236,9 +3208,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (op->customdata) { MEM_freeN(op->customdata); } - if (G.debug & G_DEBUG) { - printf("\tGP - no valid data\n"); - } return OPERATOR_CANCELLED; } @@ -3727,18 +3696,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) ARegion *current_region = BKE_area_find_region_xy( p->area, RGN_TYPE_ANY, event->x, event->y); - if (G.debug & G_DEBUG) { - printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n", - current_region, - p->region, - event->x, - event->y, - p->area->totrct.xmin, - p->area->totrct.ymin, - p->area->totrct.xmax, - p->area->totrct.ymax); - } - if (current_region) { /* Assume that since we found the cursor in here, it is in bounds * and that this should be the region that we begin drawing in @@ -3750,10 +3707,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); - } } } else if (p->region) { @@ -3765,10 +3718,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; - - if (G.debug & G_DEBUG) { - printf("%s: No active region found in GP Paint session data\n", __func__); - } } if (in_bounds) { diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index cf49aefe2ea..5ecb6d9a212 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -335,6 +335,9 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) gps->uv_scale = 1.0f; gps->inittime = 0.0f; + /* Set stroke caps. */ + gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type; + /* Apply the vertex color to fill. */ ED_gpencil_fill_vertex_color_set(ts, brush, gps); @@ -1827,11 +1830,6 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) { tgpi->flag = IN_CURVE_EDIT; } - else { - if (G.debug & G_DEBUG) { - printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); - } - } break; } case EVT_SPACEKEY: /* confirm */ @@ -1946,9 +1944,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e if (ELEM(tgpi->flag, IN_CURVE_EDIT)) { break; } - /* only handle mousemove if not doing numinput */ + /* Only handle mouse-move if not doing numeric-input. */ if (has_numinput == false) { - /* update position of mouse */ + /* Update position of mouse. */ copy_v2_v2(tgpi->end, tgpi->mval); copy_v2_v2(tgpi->start, tgpi->origin); if (tgpi->flag == IDLE) { diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 14caf0c08a7..869254cef3b 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -968,10 +968,7 @@ static void gpencil_brush_clone_free(tGP_BrushEditData *gso) tGPSB_CloneBrushData *data = gso->customdata; /* free strokes array */ - if (data->new_strokes) { - MEM_freeN(data->new_strokes); - data->new_strokes = NULL; - } + MEM_SAFE_FREE(data->new_strokes); /* free copybuf colormap */ if (data->new_colors) { diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 69734fa1ba8..93bae7d3614 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -33,6 +33,7 @@ #include "BLI_ghash.h" #include "BLI_lasso_2d.h" #include "BLI_math_vector.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" @@ -193,6 +194,28 @@ static void deselect_all_selected(bContext *C) CTX_DATA_END; } +static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select) +{ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + if (select) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + if (select) { + gps->flag |= GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_set(gpd, gps); + } + else { + gps->flag &= ~GP_STROKE_SELECT; + BKE_gpencil_stroke_select_index_reset(gps); + } +} + static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect) { for (int i = 0; i < gpc->tot_curve_points; i++) { @@ -513,6 +536,218 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Select Random Operator + * \{ */ + +static int gpencil_select_random_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) { + return OPERATOR_CANCELLED; + } + + const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); + const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); + const float randfac = RNA_float_get(op->ptr, "ratio"); + const int seed = WM_operator_properties_select_random_seed_increment_get(op); + const int start = (unselect_ends) ? 1 : 0; + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + + int selectmode; + if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) { + selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt); + } + else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) { + selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex); + } + else { + selectmode = ts->gpencil_selectmode_edit; + } + + bool changed = false; + int seed_iter = seed; + int stroke_idx = 0; + + if (is_curve_edit) { + GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc) + { + /* Only apply to unselected strokes (if select). */ + if (select) { + if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) { + continue; + } + } + else { + if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) { + continue; + } + } + + /* Different seed by stroke. */ + seed_iter += gps->totpoints + stroke_idx; + stroke_idx++; + + if (selectmode == GP_SELECTMODE_STROKE) { + RNG *rng = BLI_rng_new(seed_iter); + const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; + select_stroke ^= select; + /* Curve function has select parameter inverted. */ + select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke); + changed = true; + BLI_rng_free(rng); + } + else { + int elem_map_len = 0; + bGPDcurve_point **elem_map = MEM_mallocN(sizeof(*elem_map) * gpc->tot_curve_points, + __func__); + bGPDcurve_point *ptc; + for (int i = start; i < gpc->tot_curve_points; i++) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; + elem_map[elem_map_len++] = gpc_pt; + } + + BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); + const int count_select = elem_map_len * randfac; + for (int i = 0; i < count_select; i++) { + ptc = elem_map[i]; + if (select) { + ptc->flag |= GP_SPOINT_SELECT; + BEZT_SEL_ALL(&ptc->bezt); + } + else { + ptc->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&ptc->bezt); + } + } + MEM_freeN(elem_map); + + /* unselect start and end points */ + if (unselect_ends) { + bGPDcurve_point *gpc_pt = &gpc->curve_points[0]; + gpc_pt->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&gpc_pt->bezt); + + gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1]; + gpc_pt->flag &= ~GP_SPOINT_SELECT; + BEZT_DESEL_ALL(&gpc_pt->bezt); + } + + BKE_gpencil_curve_sync_selection(gpd, gps); + } + + changed = true; + } + GP_EDITABLE_CURVES_END(gps_iter); + } + else { + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + /* Only apply to unselected strokes (if select). */ + if (select) { + if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) { + continue; + } + } + else { + if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) { + continue; + } + } + + /* Different seed by stroke. */ + seed_iter += gps->totpoints + stroke_idx; + stroke_idx++; + + if (selectmode == GP_SELECTMODE_STROKE) { + RNG *rng = BLI_rng_new(seed_iter); + const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints; + bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false; + select_stroke ^= select; + select_all_stroke_points(gpd, gps, select_stroke); + changed = true; + BLI_rng_free(rng); + } + else { + int elem_map_len = 0; + bGPDspoint **elem_map = MEM_mallocN(sizeof(*elem_map) * gps->totpoints, __func__); + bGPDspoint *pt; + for (int i = start; i < gps->totpoints; i++) { + pt = &gps->points[i]; + elem_map[elem_map_len++] = pt; + } + + BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); + const int count_select = elem_map_len * randfac; + for (int i = 0; i < count_select; i++) { + pt = elem_map[i]; + if (select) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + MEM_freeN(elem_map); + + /* unselect start and end points */ + if (unselect_ends) { + pt = &gps->points[0]; + pt->flag &= ~GP_SPOINT_SELECT; + + pt = &gps->points[gps->totpoints - 1]; + pt->flag &= ~GP_SPOINT_SELECT; + } + + BKE_gpencil_stroke_sync_selection(gpd, gps); + } + + changed = true; + } + CTX_DATA_END; + } + + if (changed) { + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_random(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Random"; + ot->idname = "GPENCIL_OT_select_random"; + ot->description = "Select random points for non selected strokes"; + + /* callbacks */ + ot->exec = gpencil_select_random_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_select_random(ot); + RNA_def_boolean(ot->srna, + "unselect_ends", + false, + "Unselect Ends", + "Do not select the first and last point of the stroke"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Select Grouped Operator * \{ */ diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index ede1d3eefaa..99b8b672327 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -125,7 +125,7 @@ static void gpencil_undo_free_node(bGPundonode *undo_node) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd, false); + BKE_gpencil_free_data(undo_node->gpd, false); MEM_freeN(undo_node->gpd); } @@ -133,8 +133,6 @@ void gpencil_undo_push(bGPdata *gpd) { bGPundonode *undo_node; - // printf("\t\tGP - undo push\n"); - if (cur_node) { /* Remove all undone nodes from stack. */ undo_node = cur_node->next; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index ba3d3b584d7..5cc52303cd6 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -341,7 +341,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) return (gpl->actframe->framenum == cfra); } /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_frame_find(gpl, cfra); */ + // return BKE_gpencil_layer_frame_find(gpl, cfra); } } diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index d2d22dd38dc..61e37f20b1b 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -28,6 +28,7 @@ extern "C" { #endif struct AnimData; +struct AnimKeylistDrawList; struct FCurve; struct MaskLayer; struct Object; @@ -42,6 +43,14 @@ struct bGPDlayer; /* draw simple diamond-shape keyframe */ /* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, * immBegin(GPU_PRIM_POINTS, n), then call this n times */ +typedef struct KeyframeShaderBindings { + uint pos_id; + uint size_id; + uint color_id; + uint outline_color_id; + uint flags_id; +} KeyframeShaderBindings; + void draw_keyframe_shape(float x, float y, float size, @@ -49,11 +58,7 @@ void draw_keyframe_shape(float x, short key_type, short mode, float alpha, - unsigned int pos_id, - unsigned int size_id, - unsigned int color_id, - unsigned int outline_color_id, - unsigned int flags_id, + const KeyframeShaderBindings *sh_bindings, short handle_type, short extreme_type); @@ -61,65 +66,65 @@ void draw_keyframe_shape(float x, /* Channel Drawing ------------------ */ /* F-Curve */ -void draw_fcurve_channel(struct View2D *v2d, +void draw_fcurve_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac, int saction_flag); /* Action Group Summary */ -void draw_agroup_channel(struct View2D *v2d, +void draw_agroup_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag); /* Action Summary */ -void draw_action_channel(struct View2D *v2d, +void draw_action_channel(struct AnimKeylistDrawList *draw_list, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac, int saction_flag); /* Object Summary */ -void draw_object_channel(struct View2D *v2d, +void draw_object_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac, int saction_flag); /* Scene Summary */ -void draw_scene_channel(struct View2D *v2d, +void draw_scene_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac, int saction_flag); /* DopeSheet Summary */ -void draw_summary_channel( - struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac, int saction_flag); -/* Grease Pencil datablock summary */ -void draw_gpencil_channel(struct View2D *v2d, - struct bDopeSheet *ads, - struct bGPdata *gpd, +void draw_summary_channel(struct AnimKeylistDrawList *draw_list, + struct bAnimContext *ac, float ypos, float yscale_fac, int saction_flag); /* Grease Pencil Layer */ -void draw_gpl_channel(struct View2D *v2d, +void draw_gpl_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag); /* Mask Layer */ -void draw_masklay_channel(struct View2D *v2d, +void draw_masklay_channel(struct AnimKeylistDrawList *draw_list, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac, int saction_flag); +struct AnimKeylistDrawList *ED_keylist_draw_list_create(void); +void ED_keylist_draw_list_flush(struct AnimKeylistDrawList *draw_list, struct View2D *v2d); +void ED_keylist_draw_list_free(struct AnimKeylistDrawList *draw_list); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index be3eac66771..3a9750c1206 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -23,6 +23,8 @@ #pragma once +#include "BLI_range.h" + #ifdef __cplusplus extern "C" { #endif @@ -40,6 +42,8 @@ struct bGPDlayer; /* ****************************** Base Structs ****************************** */ +struct AnimKeylist; + /* Information about the stretch of time from current to the next column */ typedef struct ActKeyBlockInfo { /* Combination of flags from all curves. */ @@ -133,59 +137,74 @@ typedef enum eKeyframeExtremeDrawOpts { /* ******************************* Methods ****************************** */ +struct AnimKeylist *ED_keylist_create(void); +void ED_keylist_free(struct AnimKeylist *keylist); +const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); +const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, + const Range2f frame_range); +bool ED_keylist_is_empty(const struct AnimKeylist *keylist); +const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); + /* Key-data Generation --------------- */ /* F-Curve */ void fcurve_to_keylist(struct AnimData *adt, struct FCurve *fcu, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* Action Group */ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* Action */ void action_to_keylist(struct AnimData *adt, struct bAction *act, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* Cache File */ void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, - struct DLRBT_Tree *keys, - int saction_flag); + struct AnimKeylist *keylist, + const int saction_flag); /* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag); +void summary_to_keylist(struct bAnimContext *ac, + struct AnimKeylist *keylist, + const int saction_flag); /* Grease Pencil datablock summary */ void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, - struct DLRBT_Tree *keys, + struct AnimKeylist *keylist, const bool active); /* Grease Pencil Layer */ -void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); +void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keylist); /* Mask */ -void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys); +void mask_to_keylist(struct bDopeSheet *ads, + struct MaskLayer *masklay, + struct AnimKeylist *keylist); /* ActKeyColumn API ---------------- */ /* Comparator callback used for ActKeyColumns and cframe float-value pointer */ short compare_ak_cfraPtr(void *node, void *data); /* Checks if ActKeyColumn has any block data */ -bool actkeyblock_is_valid(ActKeyColumn *ac); +bool actkeyblock_is_valid(const ActKeyColumn *ac); /* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac); +int actkeyblock_get_valid_hold(const ActKeyColumn *ac); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 888dcd9d428..3141c8f707b 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -53,6 +53,7 @@ struct uiLayout; struct wmKeyConfig; struct wmOperator; struct wmOperatorType; +struct wmEvent; /* object_edit.c */ /* context.object */ @@ -199,6 +200,9 @@ void ED_object_parent(struct Object *ob, struct Object *parent, const int type, const char *substr); +char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, + struct PointerRNA *properties, + const struct wmEvent *event); /* bitflags for enter/exit editmode */ enum { diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 7ccdc49d291..1708c3598b1 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -713,9 +713,9 @@ DEF_ICON(UV_EDGESEL) DEF_ICON(UV_FACESEL) DEF_ICON(UV_ISLANDSEL) DEF_ICON(UV_SYNC_SELECT) -DEF_ICON_BLANK(240) -DEF_ICON_BLANK(241) -DEF_ICON_BLANK(242) +DEF_ICON(GP_CAPS_FLAT) +DEF_ICON(GP_CAPS_ROUND) +DEF_ICON(FIXED_SIZE) DEF_ICON(TRANSFORM_ORIGINS) DEF_ICON(GIZMO) DEF_ICON(ORIENTATION_CURSOR) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a6e465d04e8..30be3588b5a 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2577,10 +2577,7 @@ void ED_keymap_ui(struct wmKeyConfig *keyconf); void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); -bool UI_drop_color_poll(struct bContext *C, - struct wmDrag *drag, - const struct wmEvent *event, - const char **r_tooltip); +bool UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event); bool UI_context_copy_to_selected_list(struct bContext *C, struct PointerRNA *ptr, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a2b25aed582..fd75be5b847 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4017,9 +4017,11 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) UNUSED_VARS_NDEBUG(found_layout); ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but); } +#ifdef WITH_PYTHON if (UI_editsource_enable_check()) { UI_editsource_but_replace(old_but_ptr, but); } +#endif } return but; @@ -6171,7 +6173,7 @@ int UI_but_return_value_get(uiBut *but) void UI_but_drag_set_id(uiBut *but, ID *id) { but->dragtype = WM_DRAG_ID; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6198,7 +6200,7 @@ void UI_but_drag_set_asset(uiBut *but, but->dragtype = WM_DRAG_ASSET; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); } but->dragpoin = asset_drag; @@ -6210,7 +6212,7 @@ void UI_but_drag_set_asset(uiBut *but, void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) { but->dragtype = WM_DRAG_RNA; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6220,7 +6222,7 @@ void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr) void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) { but->dragtype = WM_DRAG_PATH; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6233,7 +6235,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free) void UI_but_drag_set_name(uiBut *but, const char *name) { but->dragtype = WM_DRAG_NAME; - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } @@ -6251,7 +6253,7 @@ void UI_but_drag_set_image( { but->dragtype = WM_DRAG_PATH; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ - if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) { + if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); but->dragflag &= ~UI_BUT_DRAGPOIN_FREE; } diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c index dbfdfbf7950..3149675ac04 100644 --- a/source/blender/editors/interface/interface_align.c +++ b/source/blender/editors/interface/interface_align.c @@ -343,7 +343,7 @@ static int ui_block_align_butal_cmp(const void *a, const void *b) * stupid UI code produces widgets which have the same TOP and LEFT positions... * We do not care really, * because this happens when UI is way too small to be usable anyway. */ - /* BLI_assert(0); */ + // BLI_assert(0); return 0; } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index d917534895d..b953d88c896 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -373,13 +373,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um) BLI_assert(ui_but_is_user_menu_compatible(C, but)); char drawstr[sizeof(but->drawstr)]; - STRNCPY(drawstr, but->drawstr); - if (but->flag & UI_BUT_HAS_SEP_CHAR) { - char *sep = strrchr(drawstr, UI_SEP_CHAR); - if (sep) { - *sep = '\0'; - } - } + ui_but_drawstr_without_sep_char(but, drawstr, sizeof(drawstr)); MenuType *mt = NULL; if (but->optype) { @@ -952,7 +946,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) { + if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 65104885d98..ebebf69bc11 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -2281,7 +2281,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, immVertex2fv(pos, v3); /* corner shape */ - /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */ immVertex2fv(pos, v3); immAttr4ub(color, 0, 0, 0, 0); immVertex2fv(pos, v4); @@ -2293,7 +2293,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, immVertex2fv(pos, v3); /* bottom quad */ - /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */ + // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */ immVertex2fv(pos, v3); immAttr4ub(color, 0, 0, 0, 0); immVertex2fv(pos, v6); diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index 8762a4819d4..ccf0e727da8 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -84,10 +84,7 @@ static void driverdropper_exit(bContext *C, wmOperator *op) { WM_cursor_modal_restore(CTX_wm_window(C)); - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bfc03a95949..76f6640c714 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3086,11 +3086,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con UI_fontstyle_set(&fstyle); - if (fstyle.kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle.uifont_id, BLF_KERNING_DEFAULT); - } - ui_but_text_password_hide(password_str, but, false); if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { @@ -3141,10 +3136,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con but->pos = glyph_data[1] + but->ofs; } - if (fstyle.kerning == 1) { - BLF_disable(fstyle.uifont_id, BLF_KERNING_DEFAULT); - } - ui_but_text_password_hide(password_str, but, true); } @@ -3438,10 +3429,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER); bool no_zero_strip = false; - if (data->str) { - MEM_freeN(data->str); - data->str = NULL; - } + MEM_SAFE_FREE(data->str); #ifdef USE_DRAG_MULTINUM /* this can happen from multi-drag */ @@ -6037,7 +6025,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co * the slot menu fails to switch a second time. * * The active state of the button could be maintained some other way - * and remove this mousemove event. + * and remove this mouse-move event. */ WM_event_add_mousemove(data->window); @@ -6779,7 +6767,7 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, ui_color_picker_hsv_to_rgb(hsv, rgb); - if ((cpicker->use_luminosity_lock)) { + if (cpicker->use_luminosity_lock) { if (!is_zero_v3(rgb)) { normalize_v3_length(rgb, cpicker->luminosity_lock_value); } @@ -8376,7 +8364,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s } } - /* wait for mousemove to enable drag */ + /* Wait for mouse-move to enable drag. */ if (state == BUTTON_STATE_WAIT_DRAG) { but->flag &= ~UI_SELECT; } @@ -8635,10 +8623,7 @@ static void button_activate_exit( } /* clean up button */ - if (but->active) { - MEM_freeN(but->active); - but->active = NULL; - } + MEM_SAFE_FREE(but->active); but->flag &= ~(UI_ACTIVE | UI_SELECT); but->flag |= UI_BUT_LAST_ACTIVE; @@ -8646,9 +8631,9 @@ static void button_activate_exit( ui_but_update(but); } - /* adds empty mousemove in queue for re-init handler, in case mouse is + /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is * still over a button. We cannot just check for this ourselves because - * at this point the mouse may be over a button in another region */ + * at this point the mouse may be over a button in another region. */ if (mousemove) { WM_event_add_mousemove(CTX_wm_window(C)); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 0ffc5659191..f739830cfdb 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -299,13 +299,14 @@ static void vicon_keytype_draw_wrapper( const float yco = y + h / 2 + 0.5f; GPUVertFormat *format = immVertexFormat(); - const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( + KeyframeShaderBindings sh_bindings; + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( + sh_bindings.outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -326,11 +327,7 @@ static void vicon_keytype_draw_wrapper( key_type, KEYFRAME_SHAPE_BOTH, alpha, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, + &sh_bindings, handle_type, KEYFRAME_EXTREME_NONE); @@ -1014,7 +1011,7 @@ static void init_iconfile_list(struct ListBase *list) int index = 1; for (int i = 0; i < totfile; i++) { - if ((dir[i].type & S_IFREG)) { + if (dir[i].type & S_IFREG) { const char *filename = dir[i].relname; if (BLI_path_extension_check(filename, ".png")) { @@ -2425,6 +2422,7 @@ void UI_icon_draw_ex(float x, ImBuf *UI_icon_alert_imbuf_get(eAlertIcon icon) { #ifdef WITH_HEADLESS + UNUSED_VARS(icon); return NULL; #else const int ALERT_IMG_SIZE = 256; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6b0b8e8df8f..d61104f094e 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -1177,6 +1177,8 @@ uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; +size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen) + ATTR_NONNULL(1, 2); size_t ui_but_drawstr_len_without_sep_char(const uiBut *but); size_t ui_but_tip_len_only_first_line(const uiBut *but); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 03c67e9b046..ec5a30f7793 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -282,7 +282,7 @@ static int ui_layout_vary_direction(uiLayout *layout) static bool ui_layout_variable_size(uiLayout *layout) { - /* Note that this code is probably a bit flakey, we'd probably want to know whether it's + /* Note that this code is probably a bit flaky, we'd probably want to know whether it's * variable in X and/or Y, etc. But for now it mimics previous one, * with addition of variable flag set for children of grid-flow layouts. */ return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 3ab49b8773b..dd10d942fc9 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -998,55 +998,69 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) UI_context_active_but_prop_get(C, &ptr, &prop, &index); /* if there is a valid property that is editable... */ - if (ptr.data && prop) { - char *path = NULL; - bool use_path_from_id; - ListBase lb = {NULL}; - - if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) && - !BLI_listbase_is_empty(&lb)) { - LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { - if (link->ptr.data != ptr.data) { - if (use_path_from_id) { - /* Path relative to ID. */ - lprop = NULL; - RNA_id_pointer_create(link->ptr.owner_id, &idptr); - RNA_path_resolve_property(&idptr, path, &lptr, &lprop); - } - else if (path) { - /* Path relative to elements from list. */ - lprop = NULL; - RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop); - } - else { - lptr = link->ptr; - lprop = prop; - } + if (ptr.data == NULL || prop == NULL) { + return false; + } - if (lptr.data == ptr.data) { - /* lptr might not be the same as link->ptr! */ - continue; - } + char *path = NULL; + bool use_path_from_id; + ListBase lb = {NULL}; - if (lprop == prop) { - if (RNA_property_editable(&lptr, lprop)) { - if (poll) { - success = true; - break; - } - if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) { - RNA_property_update(C, &lptr, prop); - success = true; - } - } - } - } - } - } + if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) { + return false; + } + if (BLI_listbase_is_empty(&lb)) { MEM_SAFE_FREE(path); - BLI_freelistN(&lb); + return false; } + LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { + if (link->ptr.data == ptr.data) { + continue; + } + + if (use_path_from_id) { + /* Path relative to ID. */ + lprop = NULL; + RNA_id_pointer_create(link->ptr.owner_id, &idptr); + RNA_path_resolve_property(&idptr, path, &lptr, &lprop); + } + else if (path) { + /* Path relative to elements from list. */ + lprop = NULL; + RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop); + } + else { + lptr = link->ptr; + lprop = prop; + } + + if (lptr.data == ptr.data) { + /* lptr might not be the same as link->ptr! */ + continue; + } + + if (lprop != prop) { + continue; + } + + if (!RNA_property_editable(&lptr, lprop)) { + continue; + } + + if (poll) { + success = true; + break; + } + if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) { + RNA_property_update(C, &lptr, prop); + success = true; + } + } + + MEM_SAFE_FREE(path); + BLI_freelistN(&lb); + return success; } @@ -1557,7 +1571,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) } /* Try to find a valid po file for current language... */ edittranslation_find_po_file(root, uilng, popath, FILE_MAX); - /* printf("po path: %s\n", popath); */ + // printf("po path: %s\n", popath); if (popath[0] == '\0') { BKE_reportf( op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); @@ -1759,10 +1773,7 @@ static void UI_OT_button_string_clear(wmOperatorType *ot) /** \name Drop Color Operator * \{ */ -bool UI_drop_color_poll(struct bContext *C, - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +bool UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) { /* should only return true for regions that include buttons, for now * return true always */ diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 97d01ac3763..a64797af24f 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1447,10 +1447,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) is_alpha = (region->overlap && (theme_col_back[3] != 255)); - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - BLF_enable(fontid, BLF_ROTATION); BLF_rotation(fontid, M_PI_2); // UI_fontstyle_set(&style->widget); @@ -1620,10 +1616,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) GPU_line_smooth(false); BLF_disable(fontid, BLF_ROTATION); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } #undef TABS_PADDING_BETWEEN_FACTOR diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8534c95b6fd..09429bb6df5 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -23,6 +23,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_screen_types.h" @@ -553,6 +554,12 @@ size_t ui_but_drawstr_len_without_sep_char(const uiBut *but) return strlen(but->drawstr); } +size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen) +{ + size_t str_len_clip = ui_but_drawstr_len_without_sep_char(but); + return BLI_strncpy_rlen(str, but->drawstr, min_zz(str_len_clip + 1, str_maxlen)); +} + size_t ui_but_tip_len_only_first_line(const uiBut *but) { if (but->tip == NULL) { diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 10bc3760b42..a8f289702f8 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1175,9 +1175,6 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, data->wrap_width = min_ii(UI_TIP_MAXWIDTH * U.pixelsize / aspect, winx - (UI_TIP_PADDING * 2)); font_flag |= BLF_WORD_WRAP; - if (data->fstyle.kerning == 1) { - font_flag |= BLF_KERNING_DEFAULT; - } BLF_enable(data->fstyle.uifont_id, font_flag); BLF_enable(blf_mono_font, font_flag); BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 88ab6a377d0..804156ba48c 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -83,7 +83,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->paneltitle.uifont_id = uifont_id; style->paneltitle.points = UI_DEFAULT_TITLE_POINTS; - style->paneltitle.kerning = 1; style->paneltitle.shadow = 3; style->paneltitle.shadx = 0; style->paneltitle.shady = -1; @@ -92,7 +91,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->grouplabel.uifont_id = uifont_id; style->grouplabel.points = UI_DEFAULT_TITLE_POINTS; - style->grouplabel.kerning = 1; style->grouplabel.shadow = 3; style->grouplabel.shadx = 0; style->grouplabel.shady = -1; @@ -101,7 +99,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->widgetlabel.uifont_id = uifont_id; style->widgetlabel.points = UI_DEFAULT_TEXT_POINTS; - style->widgetlabel.kerning = 1; style->widgetlabel.shadow = 3; style->widgetlabel.shadx = 0; style->widgetlabel.shady = -1; @@ -110,7 +107,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id style->widget.uifont_id = uifont_id; style->widget.points = UI_DEFAULT_TEXT_POINTS; - style->widget.kerning = 1; style->widget.shadow = 1; style->widget.shady = -1; style->widget.shadowalpha = 0.5f; @@ -164,9 +160,6 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, BLF_shadow(fs->uifont_id, fs->shadow, shadow_color); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } - if (fs->kerning == 1) { - font_flag |= BLF_KERNING_DEFAULT; - } if (fs_params->word_wrap == 1) { font_flag |= BLF_WORD_WRAP; } @@ -278,19 +271,12 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_disable(fs->uifont_id, BLF_ROTATION); BLF_disable(fs->uifont_id, BLF_CLIPPING); if (fs->shadow) { BLF_disable(fs->uifont_id, BLF_SHADOW); } - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -302,18 +288,10 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs, void UI_fontstyle_draw_simple( const uiFontStyle *fs, float x, float y, const char *str, const uchar col[4]) { - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); BLF_position(fs->uifont_id, x, y, 0.0f); BLF_color4ubv(fs->uifont_id, col); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -326,10 +304,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, const float col_fg[4], const float col_bg[4]) { - if (fs->kerning == 1) { - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); { @@ -357,10 +331,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, BLF_position(fs->uifont_id, x, y, 0.0f); BLF_color4fv(fs->uifont_id, col_fg); BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } } /* ************** helpers ************************ */ @@ -405,21 +375,8 @@ const uiStyle *UI_style_get_dpi(void) int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) { - int width; - - if (fs->kerning == 1) { - /* for BLF_width */ - BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - UI_fontstyle_set(fs); - width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (fs->kerning == 1) { - BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); - } - - return width; + return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } int UI_fontstyle_height_max(const uiFontStyle *fs) diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index b4ba6a7feab..9b601727e29 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -44,7 +44,7 @@ #include "interface_intern.h" struct AssetViewListData { - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; bScreen *screen; }; @@ -62,7 +62,7 @@ static void asset_view_item_but_drag_set(uiBut *but, /* Context can be null here, it's only needed for a File Browser specific hack that should go * away before too long. */ ED_asset_handle_get_full_library_path( - nullptr, &list_data->asset_library, asset_handle, blend_path); + nullptr, &list_data->asset_library_ref, asset_handle, blend_path); if (blend_path[0]) { ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle); @@ -136,7 +136,7 @@ static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) } } - if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) { + if (ED_assetlist_listen(&list_data->asset_library_ref, params->notifier)) { ED_region_tag_redraw(params->region); } } @@ -153,7 +153,7 @@ uiListType *UI_UL_asset_view() } static void asset_view_template_refresh_asset_collection( - const AssetLibraryReference &asset_library, + const AssetLibraryReference &asset_library_ref, const AssetFilterSettings &filter_settings, PointerRNA &assets_dataptr, const char *assets_propname) @@ -175,7 +175,7 @@ static void asset_view_template_refresh_asset_collection( RNA_property_collection_clear(&assets_dataptr, assets_prop); - ED_assetlist_iterate(&asset_library, [&](AssetHandle asset) { + ED_assetlist_iterate(&asset_library_ref, [&](AssetHandle asset) { if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) { /* Don't do anything else, but return true to continue iterating. */ return true; @@ -216,25 +216,25 @@ void uiTemplateAssetView(uiLayout *layout, PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr, asset_library_propname); - AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value( + AssetLibraryReference asset_library_ref = ED_asset_library_reference_from_enum_value( RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); uiLayout *row = uiLayoutRow(col, true); uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); - if (asset_library.type != ASSET_LIBRARY_LOCAL) { + if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); } - ED_assetlist_storage_fetch(&asset_library, C); - ED_assetlist_ensure_previews_job(&asset_library, C); - const int tot_items = ED_assetlist_size(&asset_library); + ED_assetlist_storage_fetch(&asset_library_ref, C); + ED_assetlist_ensure_previews_job(&asset_library_ref, C); + const int tot_items = ED_assetlist_size(&asset_library_ref); asset_view_template_refresh_asset_collection( - asset_library, *filter_settings, *assets_dataptr, assets_propname); + asset_library_ref, *filter_settings, *assets_dataptr, assets_propname); AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data), "AssetViewListData"); - list_data->asset_library = asset_library; + list_data->asset_library_ref = asset_library_ref; list_data->screen = CTX_wm_screen(C); /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 766840909cc..0e5a6a79137 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -84,6 +84,8 @@ #include "ED_screen.h" #include "ED_undo.h" +#include "RE_engine.h" + #include "RNA_access.h" #include "WM_api.h" @@ -2621,6 +2623,72 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) ED_object_constraint_active_set(ob_v, con_v); } +static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v) +{ + PointerRNA op_ptr; + uiLayout *row; + bConstraint *con = (bConstraint *)con_v; + + PointerRNA ptr; + Object *ob = ED_object_active_context(C); + + RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr); + uiLayoutSetContextPointer(layout, "constraint", &ptr); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + uiLayoutSetUnitsX(layout, 4.0f); + + /* Apply. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + ICON_CHECKMARK, + "CONSTRAINT_OT_apply"); + + /* Duplicate. */ + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"), + ICON_DUPLICATE, + "CONSTRAINT_OT_copy"); + + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), + 0, + "CONSTRAINT_OT_copy_to_selected"); + + uiItemS(layout); + + /* Move to first. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to First"), + ICON_TRIA_UP, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", 0); + if (!con->prev) { + uiLayoutSetEnabled(row, false); + } + + /* Move to last. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "CONSTRAINT_OT_move_to_index", + IFACE_("Move to Last"), + ICON_TRIA_DOWN, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + ListBase *constraint_list = ED_object_constraint_list_from_constraint(ob, con, NULL); + RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1); + if (!con->next) { + uiLayoutSetEnabled(row, false); + } +} + static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con) { bPoseChannel *pchan = BKE_pose_channel_active(ob); @@ -2652,11 +2720,13 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); + uiLayout *row = uiLayoutRow(layout, true); + if (proxy_protected == 0) { - uiItemR(layout, &ptr, "name", 0, "", ICON_NONE); + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(layout, con->name, ICON_NONE); + uiItemL(row, con->name, ICON_NONE); } /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ @@ -2697,22 +2767,22 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co UI_block_emboss_set(block, UI_EMBOSS); } else { - /* enabled */ - UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS); - uiItemR(layout, &ptr, "mute", 0, "", 0); - UI_block_emboss_set(block, UI_EMBOSS); + /* Enabled eye icon. */ + uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE); - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + /* Extra operators menu. */ + uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con); /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ - UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete"); - UI_block_emboss_set(block, UI_EMBOSS); - - /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ - uiItemS(layout); + sub = uiLayoutRow(row, false); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); + uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT); + uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete"); } + /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */ + uiItemS(layout); + /* Set but-locks for protected settings (magic numbers are used here!) */ if (proxy_protected) { UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint")); @@ -6395,6 +6465,41 @@ void uiTemplateCacheFile(uiLayout *layout, row = uiLayoutRow(layout, false); uiItemR(row, &fileptr, "is_sequence", 0, NULL, ICON_NONE); + /* Only enable render procedural option if the active engine supports it. */ + const struct RenderEngineType *engine_type = CTX_data_engine_type(C); + + Scene *scene = CTX_data_scene(C); + const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type, + scene); + + if (!engine_supports_procedural) { + row = uiLayoutRow(layout, false); + /* For Cycles, verify that experimental features are enabled. */ + if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { + uiItemL(row, + "The Cycles Alembic Procedural is only available with the experimental feature set", + ICON_INFO); + } + else { + uiItemL(row, "The active render engine does not have an Alembic Procedural", ICON_INFO); + } + } + + row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, engine_supports_procedural); + uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE); + + const bool use_render_procedural = RNA_boolean_get(&fileptr, "use_render_procedural"); + const bool use_prefetch = RNA_boolean_get(&fileptr, "use_prefetch"); + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, use_render_procedural); + uiItemR(row, &fileptr, "use_prefetch", 0, NULL, ICON_NONE); + + sub = uiLayoutRow(layout, false); + uiLayoutSetEnabled(sub, use_prefetch && use_render_procedural); + uiItemR(sub, &fileptr, "prefetch_cache_size", 0, NULL, ICON_NONE); + row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame")); sub = uiLayoutRow(row, true); uiLayoutSetPropDecorate(sub, false); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index d3481c449ac..48f638dac33 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1576,11 +1576,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle, /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - float strwidth = BLF_width(fstyle->uifont_id, str, max_len); if ((okwidth > 0.0f) && (strwidth > okwidth)) { @@ -1674,10 +1669,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle, strwidth = BLF_width(fstyle->uifont_id, str, max_len); } - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - BLI_assert(strwidth <= okwidth); return strwidth; @@ -1736,11 +1727,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - /* define ofs dynamically */ if (but->ofs > but->pos) { but->ofs = but->pos; @@ -1785,10 +1771,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct } } } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } /** @@ -1806,11 +1788,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr)); but->ofs = 0; @@ -1870,10 +1847,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons but->strwidth = strwidth; but->drawstr[drawstr_len] = 0; } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } #ifdef WITH_INPUT_IME @@ -1985,11 +1958,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, align = UI_STYLE_TEXT_CENTER; } - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - /* Special case: when we're entering text for multiple buttons, * don't draw the text for any of the multi-editing buttons */ if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) { @@ -2151,10 +2119,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, #endif } - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - #if 0 ui_rasterpos_safe(x, y, but->aspect); transopts = ui_translate_buttons(); @@ -2232,10 +2196,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, } if (ul_index != -1) { - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2)); struct UnderlineData ul_data = { @@ -2256,10 +2216,6 @@ static void widget_draw_text(const uiFontStyle *fstyle, BLF_position(fstyle->uifont_id, pos_x, pos_y, 0.0f); BLF_color4ubv(fstyle->uifont_id, wcol->text); BLF_draw(fstyle->uifont_id, "_", 2); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } } } @@ -5369,11 +5325,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, /* need to set this first */ UI_fontstyle_set(fstyle); - if (fstyle->kerning == 1) { - /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - if (separator_type == UI_MENU_ITEM_SEPARATOR_SHORTCUT) { /* Shrink rect to exclude the shortcut string. */ rect->xmax -= BLF_width(fstyle->uifont_id, cpoin + 1, INT_MAX) + UI_DPI_ICON_SIZE; @@ -5398,10 +5349,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, else { BLI_assert_msg(0, "Unknwon menu item separator type"); } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } } } diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 12890552b1d..bbff37221e8 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -615,6 +615,7 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "set_frame_range", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "is_sequence", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE); + uiItemR(col, imfptr, "always_add_cache_reader", 0, NULL, ICON_NONE); } static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) @@ -645,6 +646,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence"); const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + const bool always_add_cache_reader = RNA_boolean_get(op->ptr, "always_add_cache_reader"); const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job"); int offset = 0; @@ -672,6 +674,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op) sequence_len, offset, validate_meshes, + always_add_cache_reader, as_background_job); return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; @@ -722,6 +725,13 @@ void WM_OT_alembic_import(wmOperatorType *ot) "Check imported mesh objects for invalid data (slow)"); RNA_def_boolean(ot->srna, + "always_add_cache_reader", + false, + "Always Add Cache Reader", + "Add cache modifiers and constraints to imported objects even if they are not " + "animated so that they can be updated when reloading the Alembic archive"); + + RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence", diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index 9fa34a1c55d..b2788ee49a2 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -52,6 +52,7 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_alembic_export); #endif #ifdef WITH_USD + WM_operatortype_append(WM_OT_usd_import); WM_operatortype_append(WM_OT_usd_export); #endif diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 0eadb38abb5..d0007d9e5be 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -22,23 +22,30 @@ */ #ifdef WITH_USD +# include "DNA_modifier_types.h" # include "DNA_space_types.h" +# include <string.h> # include "BKE_context.h" # include "BKE_main.h" # include "BKE_report.h" +# include "BLI_blenlib.h" # include "BLI_path_util.h" # include "BLI_string.h" # include "BLI_utildefines.h" # include "BLT_translation.h" +# include "ED_object.h" + # include "MEM_guardedalloc.h" # include "RNA_access.h" # include "RNA_define.h" +# include "RNA_enum_types.h" + # include "UI_interface.h" # include "UI_resources.h" @@ -50,6 +57,8 @@ # include "io_usd.h" # include "usd.h" +# include "stdio.h" + const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {DAG_EVAL_RENDER, "RENDER", @@ -242,4 +251,274 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "are different settings for viewport and rendering"); } +/* ====== USD Import ====== */ + +static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + eUSDOperatorOptions *options = MEM_callocN(sizeof(eUSDOperatorOptions), "eUSDOperatorOptions"); + options->as_background_job = true; + op->customdata = options; + + return WM_operator_filesel(C, op, event); +} + +static int wm_usd_import_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + eUSDOperatorOptions *options = (eUSDOperatorOptions *)op->customdata; + const bool as_background_job = (options != NULL && options->as_background_job); + MEM_SAFE_FREE(op->customdata); + + const float scale = RNA_float_get(op->ptr, "scale"); + + const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); + + const bool read_mesh_uvs = RNA_boolean_get(op->ptr, "read_mesh_uvs"); + const bool read_mesh_colors = RNA_boolean_get(op->ptr, "read_mesh_colors"); + + char mesh_read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY; + if (read_mesh_uvs) { + mesh_read_flag |= MOD_MESHSEQ_READ_UV; + } + if (read_mesh_colors) { + mesh_read_flag |= MOD_MESHSEQ_READ_COLOR; + } + + const bool import_cameras = RNA_boolean_get(op->ptr, "import_cameras"); + const bool import_curves = RNA_boolean_get(op->ptr, "import_curves"); + const bool import_lights = RNA_boolean_get(op->ptr, "import_lights"); + const bool import_materials = RNA_boolean_get(op->ptr, "import_materials"); + const bool import_meshes = RNA_boolean_get(op->ptr, "import_meshes"); + const bool import_volumes = RNA_boolean_get(op->ptr, "import_volumes"); + + const bool import_subdiv = RNA_boolean_get(op->ptr, "import_subdiv"); + + const bool import_instance_proxies = RNA_boolean_get(op->ptr, "import_instance_proxies"); + + const bool import_visible_only = RNA_boolean_get(op->ptr, "import_visible_only"); + + const bool create_collection = RNA_boolean_get(op->ptr, "create_collection"); + + char *prim_path_mask = malloc(1024); + RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask); + + const bool import_guide = RNA_boolean_get(op->ptr, "import_guide"); + const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy"); + const bool import_render = RNA_boolean_get(op->ptr, "import_render"); + + const bool import_usd_preview = RNA_boolean_get(op->ptr, "import_usd_preview"); + const bool set_material_blend = RNA_boolean_get(op->ptr, "set_material_blend"); + + const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale"); + + /* TODO(makowalski): Add support for sequences. */ + const bool is_sequence = false; + int offset = 0; + int sequence_len = 1; + + /* Switch out of edit mode to avoid being stuck in it (T54326). */ + Object *obedit = CTX_data_edit_object(C); + if (obedit) { + ED_object_mode_set(C, OB_MODE_EDIT); + } + + const bool validate_meshes = false; + const bool use_instancing = false; + + struct USDImportParams params = {.scale = scale, + .is_sequence = is_sequence, + .set_frame_range = set_frame_range, + .sequence_len = sequence_len, + .offset = offset, + .validate_meshes = validate_meshes, + .mesh_read_flag = mesh_read_flag, + .import_cameras = import_cameras, + .import_curves = import_curves, + .import_lights = import_lights, + .import_materials = import_materials, + .import_meshes = import_meshes, + .import_volumes = import_volumes, + .prim_path_mask = prim_path_mask, + .import_subdiv = import_subdiv, + .import_instance_proxies = import_instance_proxies, + .create_collection = create_collection, + .import_guide = import_guide, + .import_proxy = import_proxy, + .import_render = import_render, + .import_visible_only = import_visible_only, + .use_instancing = use_instancing, + .import_usd_preview = import_usd_preview, + .set_material_blend = set_material_blend, + .light_intensity_scale = light_intensity_scale}; + + const bool ok = USD_import(C, filename, ¶ms, as_background_job); + + return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + struct PointerRNA *ptr = op->ptr; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + uiLayout *box = uiLayoutBox(layout); + uiLayout *col = uiLayoutColumnWithHeading(box, true, IFACE_("Data Types")); + uiItemR(col, ptr, "import_cameras", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_curves", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_lights", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_materials", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_meshes", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_volumes", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "prim_path_mask", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Mesh Data")); + uiItemR(col, ptr, "read_mesh_uvs", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "read_mesh_colors", 0, NULL, ICON_NONE); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Include")); + uiItemR(col, ptr, "import_subdiv", 0, IFACE_("Subdivision"), ICON_NONE); + uiItemR(col, ptr, "import_instance_proxies", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_visible_only", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_guide", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_proxy", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_render", 0, NULL, ICON_NONE); + + col = uiLayoutColumnWithHeading(box, true, IFACE_("Options")); + uiItemR(col, ptr, "set_frame_range", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental")); + uiItemR(col, ptr, "import_usd_preview", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(col, RNA_boolean_get(ptr, "import_materials")); + uiLayout *row = uiLayoutRow(col, true); + uiItemR(row, ptr, "set_material_blend", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(row, RNA_boolean_get(ptr, "import_usd_preview")); +} + +void WM_OT_usd_import(struct wmOperatorType *ot) +{ + ot->name = "Import USD"; + ot->description = "Import USD stage into current scene"; + ot->idname = "WM_OT_usd_import"; + + ot->invoke = wm_usd_import_invoke; + ot->exec = wm_usd_import_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_usd_import_draw; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_USD, + FILE_BLENDER, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, + FILE_DEFAULTDISPLAY, + FILE_SORT_ALPHA); + + RNA_def_float( + ot->srna, + "scale", + 1.0f, + 0.0001f, + 1000.0f, + "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, + 1000.0f); + + RNA_def_boolean(ot->srna, + "set_frame_range", + true, + "Set Frame Range", + "Update the scene's start and end frame to match those of the USD archive"); + + RNA_def_boolean(ot->srna, "import_cameras", true, "Cameras", ""); + RNA_def_boolean(ot->srna, "import_curves", true, "Curves", ""); + RNA_def_boolean(ot->srna, "import_lights", true, "Lights", ""); + RNA_def_boolean(ot->srna, "import_materials", true, "Materials", ""); + RNA_def_boolean(ot->srna, "import_meshes", true, "Meshes", ""); + RNA_def_boolean(ot->srna, "import_volumes", true, "Volumes", ""); + + RNA_def_boolean(ot->srna, + "import_subdiv", + false, + "Import Subdivision Scheme", + "Create subdivision surface modifiers based on the USD " + "SubdivisionScheme attribute"); + + RNA_def_boolean(ot->srna, + "import_instance_proxies", + true, + "Import Instance Proxies", + "Create unique Blender objects for USD instances"); + + RNA_def_boolean(ot->srna, + "import_visible_only", + true, + "Visible Primitives Only", + "Do not import invisible USD primitives. " + "Only applies to primitives with a non-animated visibility attribute. " + "Primitives with animated visibility will always be imported"); + + RNA_def_boolean(ot->srna, + "create_collection", + false, + "Create Collection", + "Add all imported objects to a new collection"); + + RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates"); + + RNA_def_boolean(ot->srna, "read_mesh_colors", false, "Vertex Colors", "Read mesh vertex colors"); + + RNA_def_string(ot->srna, + "prim_path_mask", + NULL, + 1024, + "Path Mask", + "Import only the subset of the USD scene rooted at the given primitive"); + + RNA_def_boolean(ot->srna, "import_guide", false, "Guide", "Import guide geometry"); + + RNA_def_boolean(ot->srna, "import_proxy", true, "Proxy", "Import proxy geometry"); + + RNA_def_boolean(ot->srna, "import_render", true, "Render", "Import final render geometry"); + + RNA_def_boolean(ot->srna, + "import_usd_preview", + false, + "Import USD Preview", + "Convert UsdPreviewSurface shaders to Principled BSDF shader networks"); + + RNA_def_boolean(ot->srna, + "set_material_blend", + true, + "Set Material Blend", + "If the Import USD Preview option is enabled, " + "the material blend method will automatically be set based on the " + "shader's opacity and opacityThreshold inputs"); + + RNA_def_float(ot->srna, + "light_intensity_scale", + 1.0f, + 0.0001f, + 10000.0f, + "Light Intensity Scale", + "Scale for the intensity of imported lights", + 0.0001f, + 1000.0f); +} + #endif /* WITH_USD */ diff --git a/source/blender/editors/io/io_usd.h b/source/blender/editors/io/io_usd.h index 671984b6f34..7424cc0df32 100644 --- a/source/blender/editors/io/io_usd.h +++ b/source/blender/editors/io/io_usd.h @@ -26,3 +26,5 @@ struct wmOperatorType; void WM_OT_usd_export(struct wmOperatorType *ot); + +void WM_OT_usd_import(struct wmOperatorType *ot); diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index ad71f4d9da7..110f4541e8f 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -521,7 +521,7 @@ static int add_vertex_exec(bContext *C, wmOperator *op) MaskLayer *mask_layer = BKE_mask_layer_active(mask); - if (mask_layer && mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer && mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { mask_layer = NULL; } diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index de8ea8e21eb..22232e9c87e 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -191,7 +191,7 @@ static void draw_spline_points(const bContext *C, const char draw_type) { const bool is_spline_sel = (spline->flag & SELECT) && - (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; uchar rgb_spline[4]; @@ -529,7 +529,7 @@ static void draw_spline_curve(const bContext *C, uchar rgb_tmp[4]; const bool is_spline_sel = (spline->flag & SELECT) && - (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0; @@ -604,7 +604,7 @@ static void draw_mask_layers(const bContext *C, mask_layer = mask_layer->next, i++) { const bool is_active = (i == mask->masklay_act); - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -613,7 +613,7 @@ static void draw_mask_layers(const bContext *C, /* draw curve itself first... */ draw_spline_curve(C, mask_layer, spline, draw_flag, draw_type, is_active, width, height); - if (!(mask_layer->restrictflag & MASK_RESTRICT_SELECT)) { + if (!(mask_layer->visibility_flag & MASK_HIDE_SELECT)) { /* ...and then handles over the curve so they're nicely visible */ draw_spline_points(C, mask_layer, spline, draw_flag, draw_type); } diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 6fa7457ce14..fd5925bbd0c 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -287,7 +287,7 @@ static bool spline_under_mouse_get(const bContext *C, } for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } @@ -1322,7 +1322,7 @@ static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1403,7 +1403,7 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) MaskSpline *spline; int mask_layer_shape_ofs = 0; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1523,7 +1523,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { bool changed_layer = false; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1581,7 +1581,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { bool changed_layer = false; - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1642,7 +1642,7 @@ static int set_handle_type_exec(bContext *C, wmOperator *op) bool changed = false; LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -1724,9 +1724,9 @@ static int mask_hide_view_clear_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & OB_RESTRICT_VIEWPORT) { + if (mask_layer->visibility_flag & OB_HIDE_VIEWPORT) { ED_mask_layer_select_set(mask_layer, select); - mask_layer->restrictflag &= ~OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag &= ~OB_HIDE_VIEWPORT; changed = true; } } @@ -1766,7 +1766,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } @@ -1774,7 +1774,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) if (ED_mask_layer_select_check(mask_layer)) { ED_mask_layer_select_set(mask_layer, false); - mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag |= OB_HIDE_VIEWPORT; changed = true; if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); @@ -1783,7 +1783,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op) } else { if (!ED_mask_layer_select_check(mask_layer)) { - mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; + mask_layer->visibility_flag |= OB_HIDE_VIEWPORT; changed = true; if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); @@ -1825,7 +1825,7 @@ static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op)) bool changed = false; LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) { + if (mask_layer->visibility_flag & (MASK_HIDE_SELECT | MASK_HIDE_VIEW)) { continue; } diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index cd51026d20c..e66c4e45e27 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -85,7 +85,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, *mask_layer_eval = mask_eval->masklayers.first; mask_layer_orig != NULL; mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -245,7 +245,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, mask_layer_orig != NULL; mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -409,7 +409,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, int i, tot_feather_point; float(*feather_points)[2], (*fp)[2]; - if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -638,7 +638,7 @@ bool ED_mask_selected_minmax(const bContext *C, INIT_MINMAX2(min, max); for (MaskLayer *mask_layer = mask_eval->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c index 971e1c948c9..9c4740b3087 100644 --- a/source/blender/editors/mask/mask_relationships.c +++ b/source/blender/editors/mask/mask_relationships.c @@ -46,7 +46,7 @@ static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -138,7 +138,7 @@ static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) } LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 3bb05a27c54..6a1be8dcef3 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -68,7 +68,7 @@ bool ED_mask_spline_select_check(const MaskSpline *spline) bool ED_mask_layer_select_check(const MaskLayer *mask_layer) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { return false; } @@ -110,7 +110,7 @@ void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select) void ED_mask_layer_select_set(MaskLayer *mask_layer, const bool do_select) { - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { if (do_select == true) { return; } @@ -134,7 +134,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -142,7 +142,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action) /* we don't have generic functions for this, its restricted to this operator * if one day we need to re-use such functionality, they can be split out */ - if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->visibility_flag & MASK_HIDE_SELECT) { continue; } LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { @@ -164,9 +164,9 @@ void ED_mask_select_flush_all(Mask *mask) LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { spline->flag &= ~SELECT; - /* intentionally _dont_ do this in the mask layer loop - * so we clear flags on all splines */ - if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { + /* Intentionally *don't* do this in the mask layer loop + * so we clear flags on all splines. */ + if (mask_layer->visibility_flag & MASK_HIDE_VIEW) { continue; } @@ -465,7 +465,7 @@ static int box_select_exec(bContext *C, wmOperator *op) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -550,7 +550,7 @@ static bool do_lasso_select_mask(bContext *C, /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -691,7 +691,7 @@ static int circle_select_exec(bContext *C, wmOperator *op) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -820,7 +820,7 @@ static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -870,7 +870,7 @@ static int mask_select_more_less(bContext *C, bool more) Mask *mask = CTX_data_edit_mask(C); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index a5a3489c143..6620096c39a 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -144,7 +144,7 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op) LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -238,7 +238,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) const bool do_location = RNA_boolean_get(op->ptr, "location"); LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 38d530ba911..1af489b60ce 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -468,7 +468,9 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt) gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool; gzgt->setup = gizmo_mesh_spin_init_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + /* This works well with right click selection but overrides left-mouse selection + * when clicking which is needed to create a full 360 degree revolution, see: T89912. */ + // gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; gzgt->refresh = gizmo_mesh_spin_init_refresh; gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe; gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 73f6a3f3238..3e3593d18fd 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2630,24 +2630,24 @@ static void knife_init_colors(KnifeColors *colors) } /* called when modal loop selection gets set up... */ -static void knifetool_init(bContext *C, +static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, const bool is_interactive) { - Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); + kcd->vc = *vc; + + Scene *scene = vc->scene; + Object *obedit = vc->obedit; /* assign the drawing handle for drawing preview line... */ kcd->scene = scene; kcd->ob = obedit; - kcd->region = CTX_wm_region(C); + kcd->region = vc->region; invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); - em_setup_viewcontext(C, &kcd->vc); - kcd->em = BKE_editmesh_from_object(kcd->ob); /* cut all the way through the mesh if use_occlude_geometry button not pushed */ @@ -2694,14 +2694,14 @@ static void knifetool_init(bContext *C, } /* called when modal loop selection is done... */ -static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd) +static void knifetool_exit_ex(KnifeTool_OpData *kcd) { if (!kcd) { return; } if (kcd->is_interactive) { - WM_cursor_modal_restore(CTX_wm_window(C)); + WM_cursor_modal_restore(kcd->vc.win); /* deactivate the extra drawing stuff in 3D-View */ ED_region_draw_cb_exit(kcd->region->type, kcd->draw_handle); @@ -2735,10 +2735,10 @@ static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd) /* destroy kcd itself */ MEM_freeN(kcd); } -static void knifetool_exit(bContext *C, wmOperator *op) +static void knifetool_exit(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; - knifetool_exit_ex(C, kcd); + knifetool_exit_ex(kcd); op->customdata = NULL; } @@ -2827,10 +2827,10 @@ static void knifetool_finish(wmOperator *op) /** \name Operator (#MESH_OT_knife_tool) * \{ */ -static void knifetool_cancel(bContext *C, wmOperator *op) +static void knifetool_cancel(bContext *UNUSED(C), wmOperator *op) { /* this is just a wrapper around exit() */ - knifetool_exit(C, op); + knifetool_exit(op); } wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) @@ -2872,7 +2872,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) bool do_refresh = false; if (!obedit || obedit->type != OB_MESH || BKE_editmesh_from_object(obedit) != kcd->em) { - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; } @@ -2893,7 +2893,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) /* finish */ ED_region_tag_redraw(kcd->region); - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; @@ -2902,7 +2902,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_region_tag_redraw(kcd->region); knifetool_finish(op); - knifetool_exit(C, op); + knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; @@ -3066,8 +3066,11 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input"); + ViewContext vc; KnifeTool_OpData *kcd; + em_setup_viewcontext(C, &vc); + if (only_select) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -3080,7 +3083,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* alloc new customdata */ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, kcd, only_select, cut_through, true); + knifetool_init(&vc, kcd, only_select, cut_through, true); op->flag |= OP_IS_MODAL_CURSOR_REGION; @@ -3165,7 +3168,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; @@ -3176,7 +3179,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(C, kcd, only_select, cut_through, is_interactive); + knifetool_init(vc, kcd, only_select, cut_through, is_interactive); kcd->ignore_edge_snapping = true; kcd->ignore_vert_snapping = true; @@ -3313,7 +3316,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug #undef F_ISECT_SET_OUTSIDE } - knifetool_exit_ex(C, kcd); + knifetool_exit_ex(kcd); kcd = NULL; } } diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 09b17acf56d..669a09b3fd3 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -32,6 +32,8 @@ #include "BKE_curve.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_object.h" @@ -114,7 +116,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, BKE_nurbList_free(&nurbslist); if (me_eval_needs_free) { - BKE_mesh_free((struct Mesh *)me_eval); + BKE_id_free(NULL, (ID *)me_eval); } } @@ -124,21 +126,39 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, static int knifeproject_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const bool cut_through = RNA_boolean_get(op->ptr, "cut_through"); LinkNode *polys = NULL; CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - if (ob != obedit) { - polys = knifeproject_poly_from_object(C, scene, ob, polys); + if (BKE_object_is_in_editmode(ob)) { + continue; } + polys = knifeproject_poly_from_object(C, scene, ob, polys); } CTX_DATA_END; - if (polys) { - EDBM_mesh_knife(C, polys, true, cut_through); + if (polys == NULL) { + BKE_report(op->reports, + RPT_ERROR, + "No other selected objects have wire or boundary edges to use for projection"); + return OPERATOR_CANCELLED; + } + + ViewContext vc; + em_setup_viewcontext(C, &vc); + + /* TODO: Ideally meshes would occlude each other, currently they don't + * since each knife-project runs as a separate operation. */ + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + vc.view_layer, vc.v3d, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_view3d_viewcontext_init_object(&vc, obedit); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + EDBM_mesh_knife(&vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); @@ -148,16 +168,12 @@ static int knifeproject_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); BM_mesh_select_mode_flush(em->bm); - - BLI_linklist_freeN(polys); - - return OPERATOR_FINISHED; } + MEM_freeN(objects); + + BLI_linklist_freeN(polys); - BKE_report(op->reports, - RPT_ERROR, - "No other selected objects have wire or boundary edges to use for projection"); - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } void MESH_OT_knife_project(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c index 43e36957dc9..c58f29917b1 100644 --- a/source/blender/editors/mesh/editmesh_preselect_edgering.c +++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c @@ -20,6 +20,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "BLI_math.h" #include "BLI_stack.h" @@ -160,16 +162,21 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_mul(matrix); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformThemeColor3(TH_GIZMO_PRIMARY); - if (psel->edges_len > 0) { + float viewport[4]; + GPU_viewport_size_get_f(viewport); + + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniform2fv("viewportSize", &viewport[2]); + immUniformThemeColor3(TH_GIZMO_PRIMARY); + immUniform1f("lineWidth", U.pixelsize); immBegin(GPU_PRIM_LINES, psel->edges_len * 2); for (int i = 0; i < psel->edges_len; i++) { @@ -178,10 +185,18 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } immEnd(); + immUnbindProgram(); } if (psel->verts_len > 0) { - GPU_point_size(3.0f); + GPU_program_point_size(true); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformThemeColor3(TH_GIZMO_PRIMARY); + + /* Same size as an edit mode vertex */ + immUniform1f("size", + 2.0 * U.pixelsize * + (max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * (float)M_SQRT2 / 2.0f))); immBegin(GPU_PRIM_POINTS, psel->verts_len); @@ -190,14 +205,15 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl } immEnd(); + immUnbindProgram(); + GPU_program_point_size(false); } - immUnbindProgram(); - GPU_matrix_pop(); /* Reset default */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_NONE); } static void view3d_preselect_mesh_edgering_update_verts_from_edge( diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 830c9abb41e..8e38d41f971 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1860,10 +1860,16 @@ void MESH_OT_loop_select(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection"); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring"); + PropertyRNA *prop; + + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } void MESH_OT_edgering_select(wmOperatorType *ot) @@ -1880,10 +1886,16 @@ void MESH_OT_edgering_select(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring"); + /* Properties. */ + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 41a9f426798..956658bd2b7 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -467,40 +467,50 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); - BM_custom_loop_normals_to_vector_layer(em->bm); - switch (type) { case MESH_DELETE_VERT: /* Erase Vertices */ - if (!(em->bm->totvertsel && - EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))) { + if (em->bm->totvertsel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) { continue; } break; case MESH_DELETE_EDGE: /* Erase Edges */ - if (!(em->bm->totedgesel && - EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))) { + if (em->bm->totedgesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) { continue; } break; case MESH_DELETE_FACE: /* Erase Faces */ - if (!(em->bm->totfacesel && - EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))) { + if (em->bm->totfacesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) { continue; } break; - case MESH_DELETE_EDGE_FACE: - /* Edges and Faces */ - if (!((em->bm->totedgesel || em->bm->totfacesel) && - EDBM_op_callf( - em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))) { + case MESH_DELETE_EDGE_FACE: /* Edges and Faces */ + if ((em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf( + em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) { continue; } break; - case MESH_DELETE_ONLY_FACE: - /* Only faces. */ - if (!(em->bm->totfacesel && - EDBM_op_callf( - em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES))) { + case MESH_DELETE_ONLY_FACE: /* Only faces. */ + if (em->bm->totfacesel == 0) { + continue; + } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) { continue; } break; @@ -2183,6 +2193,61 @@ static bool flip_custom_normals(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr) /* -------------------------------------------------------------------- */ /** \name Flip Normals Operator * \{ */ + +static void edbm_flip_normals_custom_loop_normals(Object *obedit, BMEditMesh *em) +{ + if (!CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) { + return; + } + + /* The mesh has custom normal data, flip them. */ + BMesh *bm = em->bm; + + BM_lnorspace_update(bm); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + negate_v3(lnor_ed->nloc); + + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + BM_loop_normal_editdata_array_free(lnors_ed_arr); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); +} + +static void edbm_flip_normals_face_winding(wmOperator *op, Object *obedit, BMEditMesh *em) +{ + + bool has_flipped_faces = false; + + /* See if we have any custom normals to flip. */ + BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm); + + if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) { + has_flipped_faces = true; + } + + if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) { + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = false, + }); + } + + if (lnors_ed_arr != NULL) { + BM_loop_normal_editdata_array_free(lnors_ed_arr); + } +} + static int edbm_flip_normals_exec(bContext *C, wmOperator *op) { const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors"); @@ -2197,56 +2262,16 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); if (only_clnors) { - if (CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) { - /* The mesh has custom normal data, flip them. */ - BMesh *bm = em->bm; - - BM_lnorspace_update(bm); - BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false); - BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; - - for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { - negate_v3(lnor_ed->nloc); - - BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], - lnor_ed->nloc, - lnor_ed->clnors_data); - } - BM_loop_normal_editdata_array_free(lnors_ed_arr); - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = false, - .is_destructive = false, - }); + if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { + continue; } - continue; - } - - if (em->bm->totfacesel == 0) { - continue; - } - - bool has_flipped_faces = false; - - /* See if we have any custom normals to flip. */ - BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm); - - if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) { - has_flipped_faces = true; - } - - if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) { - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = false, - .is_destructive = false, - }); + edbm_flip_normals_custom_loop_normals(obedit, em); } - - if (lnors_ed_arr != NULL) { - BM_loop_normal_editdata_array_free(lnors_ed_arr); + else { + if (em->bm->totfacesel == 0) { + continue; + } + edbm_flip_normals_face_winding(op, obedit, em); } } @@ -2748,9 +2773,6 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) { - BMIter fiter; - BMFace *f; - int tot_invalid = 0; int tot_unselected = 0; ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2777,22 +2799,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) if (em->bm->totvertsel == 0) { tot_unselected++; - tot_invalid++; - continue; - } - - bool is_invalid = false; - /* Check if select faces are triangles. */ - BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - if (f->len > 4) { - tot_invalid++; - is_invalid = true; - break; - } - } - } - if (is_invalid) { continue; } @@ -2841,10 +2847,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_WARNING, "No selected vertex"); return OPERATOR_CANCELLED; } - if (tot_invalid == objects_len) { - BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads"); - return OPERATOR_CANCELLED; - } return OPERATOR_FINISHED; } @@ -4666,7 +4668,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const int type = RNA_enum_get(op->ptr, "type"); - int retval = 0; + bool changed_multi = false; if (ED_operator_editmesh(C)) { uint bases_len = 0; @@ -4676,6 +4678,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) for (uint bs_index = 0; bs_index < bases_len; bs_index++) { Base *base = bases[bs_index]; BMEditMesh *em = BKE_editmesh_from_object(base->object); + bool changed = false; if (type == 0) { if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { @@ -4690,20 +4693,20 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) /* editmode separate */ switch (type) { case MESH_SEPARATE_SELECTED: - retval = mesh_separate_selected(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm); break; case MESH_SEPARATE_MATERIAL: - retval = mesh_separate_material(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm); break; case MESH_SEPARATE_LOOSE: - retval = mesh_separate_loose(bmain, scene, view_layer, base, em->bm); + changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm); break; default: BLI_assert(0); break; } - if (retval) { + if (changed) { EDBM_update(base->object->data, &(const struct EDBMUpdate_Params){ .calc_looptri = true, @@ -4711,6 +4714,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .is_destructive = true, }); } + changed_multi |= changed; } MEM_freeN(bases); } @@ -4727,7 +4731,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) Mesh *me = ob->data; if (!ID_IS_LINKED(me)) { BMesh *bm_old = NULL; - int retval_iter = 0; + bool changed = false; bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &((struct BMeshCreateParams){ @@ -4738,17 +4742,17 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) switch (type) { case MESH_SEPARATE_MATERIAL: - retval_iter = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old); + changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old); break; case MESH_SEPARATE_LOOSE: - retval_iter = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old); + changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old); break; default: BLI_assert(0); break; } - if (retval_iter) { + if (changed) { BM_mesh_bm_to_me(bmain, bm_old, me, @@ -4762,14 +4766,14 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) BM_mesh_free(bm_old); - retval |= retval_iter; + changed_multi |= changed; } } } CTX_DATA_END; } - if (retval) { + if (changed_multi) { /* delay depsgraph recalc until all objects are duplicated */ DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); @@ -5572,24 +5576,24 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); - bool is_face_pair; + const bool do_seam = RNA_boolean_get(op->ptr, "seam"); + const bool do_sharp = RNA_boolean_get(op->ptr, "sharp"); + const bool do_uvs = RNA_boolean_get(op->ptr, "uvs"); + const bool do_vcols = RNA_boolean_get(op->ptr, "vcols"); + const bool do_materials = RNA_boolean_get(op->ptr, "materials"); + float angle_face_threshold, angle_shape_threshold; + bool is_face_pair; { int totelem_sel[3]; EDBM_mesh_stats_multi(objects, objects_len, NULL, totelem_sel); is_face_pair = (totelem_sel[2] == 2); } - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; - float angle_face_threshold, angle_shape_threshold; + /* When joining exactly 2 faces, no limit. + * this is useful for one off joins while editing. */ + { PropertyRNA *prop; - - /* When joining exactly 2 faces, no limit. - * this is useful for one off joins while editing. */ prop = RNA_struct_find_property(op->ptr, "face_threshold"); if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) { angle_face_threshold = DEG2RADF(180.0f); @@ -5605,12 +5609,15 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) else { angle_shape_threshold = RNA_property_float_get(op->ptr, prop); } + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - do_seam = RNA_boolean_get(op->ptr, "seam"); - do_sharp = RNA_boolean_get(op->ptr, "sharp"); - do_uvs = RNA_boolean_get(op->ptr, "uvs"); - do_vcols = RNA_boolean_get(op->ptr, "vcols"); - do_materials = RNA_boolean_get(op->ptr, "materials"); + if (em->bm->totfacesel == 0) { + continue; + } BM_custom_loop_normals_to_vector_layer(em->bm); @@ -6335,7 +6342,7 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) BMesh *bm = em->bm; if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) { - return OPERATOR_CANCELLED; + continue; } /* tricky to maintain correct selection here, so just flush up from verts */ @@ -8620,7 +8627,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent * RNA_enum_set(op->ptr, "mode", mode); } - /* Only handle mousemove event in case we are in mouse mode. */ + /* Only handle mouse-move event in case we are in mouse mode. */ if (event->type == MOUSEMOVE || force_mousemove) { if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) { ARegion *region = CTX_wm_region(C); @@ -9527,6 +9534,10 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; + if (bm->totfacesel == 0) { + continue; + } + BMFace *f; BMVert *v; BMEdge *e; @@ -9538,18 +9549,11 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) BKE_editmesh_ensure_autosmooth(em, obedit->data); BKE_editmesh_lnorspace_update(em, obedit->data); - float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__); - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) { - const int v_index = BM_elem_index_get(v); - add_v3_v3(vnors[v_index], f->no); - } - } - } - for (int i = 0; i < bm->totvert; i++) { - if (!is_zero_v3(vnors[i]) && normalize_v3(vnors[i]) < CLNORS_VALID_VEC_LEN) { - zero_v3(vnors[i]); + float(*vnors)[3] = MEM_mallocN(sizeof(*vnors) * bm->totvert, __func__); + { + int v_index; + BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) { + BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vnors[v_index]); } } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index fc9e1aa8b1a..f52cd94b8dc 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -755,11 +755,11 @@ static void undomesh_free_data(UndoMesh *um) #endif if (me->key) { - BKE_key_free(me->key); + BKE_key_free_data(me->key); MEM_freeN(me->key); } - BKE_mesh_free(me); + BKE_mesh_free_data_for_undo(me); } static Object *editmesh_object_from_context(bContext *C) diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 85c646d689c..c6a8e771362 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -355,10 +355,7 @@ void EDBM_mesh_clear(BMEditMesh *em) /* free tessellation data */ em->tottri = 0; - if (em->looptris) { - MEM_freeN(em->looptris); - em->looptris = NULL; - } + MEM_SAFE_FREE(em->looptris); } void EDBM_mesh_load(Main *bmain, Object *ob) diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index b2379610f65..c075d2550cb 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -1007,15 +1007,8 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator if (me->flag & ME_AUTOSMOOTH) { float(*polynors)[3] = MEM_mallocN(sizeof(*polynors) * (size_t)me->totpoly, __func__); - BKE_mesh_calc_normals_poly(me->mvert, - NULL, - me->totvert, - me->mloop, - me->mpoly, - me->totloop, - me->totpoly, - polynors, - true); + BKE_mesh_calc_normals_poly( + me->mvert, me->totvert, me->mloop, me->totloop, me->mpoly, me->totpoly, polynors); BKE_edges_sharp_from_angle_set(me->mvert, me->totvert, diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index f25317e8e85..03c99e40d1e 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -150,7 +150,10 @@ void MESH_OT_face_split_by_edges(struct wmOperatorType *ot); /* *** editmesh_knife.c *** */ void MESH_OT_knife_tool(struct wmOperatorType *ot); void MESH_OT_knife_project(struct wmOperatorType *ot); -void EDBM_mesh_knife(struct bContext *C, struct LinkNode *polys, bool use_tag, bool cut_through); +void EDBM_mesh_knife(struct ViewContext *vc, + struct LinkNode *polys, + bool use_tag, + bool cut_through); struct wmKeyMap *knifetool_modal_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c index 5eb69aab48b..1a3e6a59588 100644 --- a/source/blender/editors/mesh/mesh_mirror.c +++ b/source/blender/editors/mesh/mesh_mirror.c @@ -365,10 +365,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em, void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store) { - if (mesh_topo_store->index_lookup) { - MEM_freeN(mesh_topo_store->index_lookup); - } - mesh_topo_store->index_lookup = NULL; + MEM_SAFE_FREE(mesh_topo_store->index_lookup); mesh_topo_store->prev_vert_tot = -1; mesh_topo_store->prev_edge_tot = -1; } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8ae74fbfafa..12b52907057 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2844,7 +2844,8 @@ static int object_convert_exec(bContext *C, wmOperator *op) matrix, 0, use_seams, - use_faces); + use_faces, + true); /* Remove unused materials. */ int actcol = ob_gpencil->actcol; @@ -3530,7 +3531,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - basen->object->restrictflag &= ~OB_RESTRICT_VIEWPORT; + basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT; int mval[2]; if (object_add_drop_xy_get(C, op, &mval)) { diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 43358f51396..0a2df655395 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -1311,7 +1311,7 @@ static int bake(const BakeAPIRender *bkr, } else { ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } } @@ -1411,7 +1411,7 @@ static int bake(const BakeAPIRender *bkr, /* initialize highpoly_data */ highpoly[i].ob = ob_iter; highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); - highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; + highpoly[i].ob_eval->visibility_flag &= ~OB_HIDE_RENDER; highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false); @@ -1427,10 +1427,10 @@ static int bake(const BakeAPIRender *bkr, BLI_assert(i == tot_highpoly); if (ob_cage != NULL) { - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->visibility_flag |= OB_HIDE_RENDER; ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); } - ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_low_eval->visibility_flag |= OB_HIDE_RENDER; ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); /* populate the pixel arrays with the corresponding face data for each high poly object */ @@ -1473,7 +1473,7 @@ static int bake(const BakeAPIRender *bkr, } else { /* If low poly is not renderable it should have failed long ago. */ - BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); + BLI_assert((ob_low_eval->visibility_flag & OB_HIDE_RENDER) == 0); if (RE_bake_has_engine(re)) { ok = RE_bake_engine(re, diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 4970338973d..e0419e0a4cc 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -432,11 +432,8 @@ static void test_constraint( * free the points array and request a rebind... */ if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { - /* free the points array */ - if (data->points) { - MEM_freeN(data->points); - data->points = NULL; - } + MEM_SAFE_FREE(data->points); + data->numpoints = 0; /* clear the bound flag, forcing a rebind next time this is evaluated */ data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; @@ -1486,13 +1483,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) /* free the constraint */ if (BKE_constraint_remove_ex(lb, ob, con, true)) { - /* there's no active constraint now, so make sure this is the case */ - BKE_constraints_active_set(&ob->constraints, NULL); /* needed to set the flags on posebones correctly */ ED_object_constraint_update(bmain, ob); /* relations */ - DEG_relations_tag_update(CTX_data_main(C)); + DEG_relations_tag_update(bmain); /* notifiers */ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); @@ -1510,10 +1505,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_constraint_invoke_properties(C, op, event, &retval)) { - return constraint_delete_exec(C, op); + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + return constraint_delete_exec(C, op); } void CONSTRAINT_OT_delete(wmOperatorType *ot) @@ -1537,6 +1532,320 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ +/** \name Apply Constraint Operator + * \{ */ + +static int constraint_apply_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + const bool is_first_constraint = con != constraints->first; + + /* Copy the constraint. */ + bool success; + if (pchan) { + success = BKE_constraint_apply_and_remove_for_pose( + depsgraph, scene, constraints, ob, con, pchan); + } + else { + success = BKE_constraint_apply_and_remove_for_object(depsgraph, scene, constraints, ob, con); + } + + if (!success) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + + /* Update for any children that may get moved. */ + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); + if (pchan) { + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + } + else { + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + } + + if (RNA_boolean_get(op->ptr, "report")) { + if (is_first_constraint) { + BKE_report(op->reports, + RPT_INFO, + "Applied constraint was not first, result may not be as expected"); + } + else { + /* Only add this report if the operator didn't cause another one. The purpose here is + * to alert that something happened, and the previous report will do that anyway. */ + BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name); + } + } + + return OPERATOR_FINISHED; +} + +static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_apply_exec(C, op); +} + +void CONSTRAINT_OT_apply(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Constraint"; + ot->idname = "CONSTRAINT_OT_apply"; + ot->description = "Apply constraint and remove from the stack"; + + /* callbacks */ + ot->invoke = constraint_apply_invoke; + ot->exec = constraint_apply_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint Operator + * \{ */ + +static int constraint_copy_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + bPoseChannel *pchan; + ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); + + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, con->name); + + /* Copy the constraint. */ + bConstraint *copy_con; + if (pchan) { + copy_con = BKE_constraint_copy_for_pose(ob, pchan, con); + } + else { + copy_con = BKE_constraint_copy_for_object(ob, con); + } + + if (!copy_con) { + /* Couldn't remove due to some invalid data. */ + return OPERATOR_CANCELLED; + } + /* Move constraint to correct position. */ + const int new_index = BLI_findindex(constraints, con) + 1; + const int current_index = BLI_findindex(constraints, copy_con); + BLI_assert(new_index >= 0); + BLI_assert(current_index >= 0); + BLI_listbase_link_move(constraints, copy_con, new_index - current_index); + + /* Needed to set the flags on posebones correctly. */ + ED_object_constraint_update(bmain, ob); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob); + + if (RNA_boolean_get(op->ptr, "report")) { + BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name); + } + + return OPERATOR_FINISHED; +} + +static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return OPERATOR_CANCELLED; + } + return constraint_copy_exec(C, op); +} + +void CONSTRAINT_OT_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Constraint"; + ot->idname = "CONSTRAINT_OT_copy"; + ot->description = "Duplicate constraint at the same position in the stack"; + + /* callbacks */ + ot->invoke = constraint_copy_invoke; + ot->exec = constraint_copy_exec; + ot->poll = edit_constraint_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); + edit_constraint_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Copy Constraint To Selected Operator + * \{ */ + +static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *obact = ED_object_active_context(C); + bConstraint *con = edit_constraint_property_get(C, op, obact, 0); + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + /* Don't do anything if bone doesn't exist or doesn't have any constraints. */ + if (pchan->constraints.first == NULL) { + BKE_report(op->reports, RPT_ERROR, "No constraints for copying"); + return OPERATOR_CANCELLED; + } + + Object *prev_ob = NULL; + + /* Copy all constraints from active posebone to all selected posebones. */ + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (pchan == chan) { + continue; + } + + BKE_constraint_copy_for_pose(ob, chan, con); + /* Update flags (need to add here, not just copy). */ + chan->constflag |= pchan->constflag; + + if (prev_ob == ob) { + continue; + } + + BKE_pose_tag_recalc(bmain, ob->pose); + DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY); + prev_ob = ob; + } + CTX_DATA_END; + } + else { + /* Copy all constraints from active object to all selected objects. */ + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + /* If we're not handling the object we're copying from, copy all constraints over. */ + if (obact == ob) { + continue; + } + + BKE_constraint_copy_for_object(ob, con); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM); + } + CTX_DATA_END; + } + + /* Force depsgraph to get recalculated since new relationships added. */ + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL); + + return OPERATOR_FINISHED; +} + +static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (!edit_constraint_invoke_properties(C, op, event, &retval)) { + return retval; + } + return constraint_copy_to_selected_exec(C, op); +} + +static bool constraint_copy_to_selected_poll(bContext *C) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); + Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + bConstraint *con = ptr.data; + bPoseChannel *pchan; + ED_object_constraint_list_from_constraint(obact, con, &pchan); + + if (pchan) { + bool found = false; + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) { + if (pchan != chan) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other bones are selected"); + return false; + } + + if (!obact) { + CTX_wm_operator_poll_msg_set(C, "No selected object to copy from"); + return false; + } + + bool found = false; + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob != obact) { + /** NOTE: Can not return here, because CTX_DATA_BEGIN allocated + * a list that needs to be freed by CTX_DATA_END. */ + found = true; + break; + } + } + CTX_DATA_END; + if (found) { + return true; + } + + CTX_wm_operator_poll_msg_set(C, "No other objects are selected"); + return false; +} + +void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Constraint To Selected"; + ot->idname = "CONSTRAINT_OT_copy_to_selected"; + ot->description = "Copy constraint to other selected objects/bones"; + + /* api callbacks */ + ot->exec = constraint_copy_to_selected_exec; + ot->invoke = constraint_copy_to_selected_invoke; + ot->poll = constraint_copy_to_selected_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + edit_constraint_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ /** \name Move Down Constraint Operator * \{ */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 6108691b2f1..5697c2c973d 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -384,7 +384,7 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); if (v3d->flag & V3D_LOCAL_COLLECTIONS) { - if (lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) { + if (lc->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) { return OPERATOR_CANCELLED; } if (toggle) { @@ -421,7 +421,7 @@ void ED_collection_hide_menu_draw(const bContext *C, uiLayout *layout) continue; } - if (lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) { + if (lc->collection->flag & COLLECTION_HIDE_VIEWPORT) { continue; } @@ -926,7 +926,7 @@ static bool editmode_toggle_poll(bContext *C) } /* if hidden but in edit mode, we still display */ - if ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) { + if ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) { return false; } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 6299fdcc7f7..10e016738d0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -226,6 +226,9 @@ void POSE_OT_ik_add(struct wmOperatorType *ot); void POSE_OT_ik_clear(struct wmOperatorType *ot); void CONSTRAINT_OT_delete(struct wmOperatorType *ot); +void CONSTRAINT_OT_apply(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy(struct wmOperatorType *ot); +void CONSTRAINT_OT_copy_to_selected(struct wmOperatorType *ot); void CONSTRAINT_OT_move_up(struct wmOperatorType *ot); void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 36a4f002978..2c58ef02486 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -585,7 +585,7 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) "use_flash_on_transfer", true, "Flash On Transfer", - "Flash the target object when transfering the mode"); + "Flash the target object when transferring the mode"); } /** \} */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index a438c760d3b..c1928cf7f8a 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -183,6 +183,9 @@ void ED_operatortypes_object(void) WM_operatortype_append(POSE_OT_ik_add); WM_operatortype_append(POSE_OT_ik_clear); WM_operatortype_append(CONSTRAINT_OT_delete); + WM_operatortype_append(CONSTRAINT_OT_apply); + WM_operatortype_append(CONSTRAINT_OT_copy); + WM_operatortype_append(CONSTRAINT_OT_copy_to_selected); WM_operatortype_append(CONSTRAINT_OT_move_up); WM_operatortype_append(CONSTRAINT_OT_move_down); WM_operatortype_append(CONSTRAINT_OT_move_to_index); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index c61965b3e23..ec72ff11683 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -570,6 +570,8 @@ void ED_object_parent_clear(Object *ob, const int type) /* clear parenting relationship completely */ ob->parent = NULL; + ob->partype = PAROBJECT; + ob->parsubstr[0] = 0; break; } case CLEAR_PARENT_KEEP_TRANSFORM: { @@ -2723,24 +2725,53 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) /** \name Drop Named Material on Object Operator * \{ */ +char *ED_object_ot_drop_named_material_tooltip(bContext *C, + PointerRNA *properties, + const wmEvent *event) +{ + Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + if (ob == NULL) { + return BLI_strdup(""); + } + + char name[MAX_ID_NAME - 2]; + RNA_string_get(properties, "name", name); + + int active_mat_slot = max_ii(ob->actcol, 1); + Material *prev_mat = BKE_object_material_get(ob, active_mat_slot); + + char *result; + if (prev_mat) { + const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)"); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2); + } + else { + const char *tooltip = TIP_("Drop %s on %s (slot %d)"); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot); + } + return result; +} + static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); - Base *base = ED_view3d_give_base_under_cursor(C, event->mval); + Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); Material *ma; char name[MAX_ID_NAME - 2]; RNA_string_get(op->ptr, "name", name); ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name); - if (base == NULL || ma == NULL) { + if (ob == NULL || ma == NULL) { return OPERATOR_CANCELLED; } - BKE_object_material_assign(CTX_data_main(C), base->object, ma, 1, BKE_MAT_ASSIGN_USERPREF); + const short active_mat_slot = ob->actcol; + + BKE_object_material_assign(CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF); - DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object); + WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 82dbc9aaf38..d56cb3c7548 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -166,9 +166,10 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh); BKE_id_free(nullptr, new_mesh); new_mesh = mesh_fixed_poles; - BKE_mesh_calc_normals(new_mesh); } + BKE_mesh_calc_normals(new_mesh); + if (mesh->flag & ME_REMESH_REPROJECT_VOLUME || mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK || mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) { BKE_mesh_runtime_clear_geometry(mesh); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index f64f95c5322..7a42c9d5d8b 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -1582,7 +1582,7 @@ static void vgroup_fix( mag = normalize_v3(norm); if (mag) { /* zeros fix */ d = -dot_v3v3(norm, coord); - /* dist = (dot_v3v3(norm, m.co) + d); */ /* UNUSED */ + // dist = (dot_v3v3(norm, m.co) + d); /* UNUSED */ moveCloserToDistanceFromPlane( depsgraph, scene_eval, object_eval, me, i, norm, coord, d, distToBe, strength, cp); } diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 5a629058c81..8afc5c583e0 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -2957,10 +2957,7 @@ static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror) } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); if (psys->child) { MEM_freeN(psys->child); @@ -3576,10 +3573,7 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); edit->totpoint = psys->totpart = newtotpart; @@ -4497,10 +4491,7 @@ static int brush_add(const bContext *C, PEData *data, short number) } edit->points = new_points; - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); /* create tree for interpolation */ if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) { @@ -4676,7 +4667,7 @@ typedef struct BrushEdit { int lastmouse[2]; float zfac; - /* optional cached view settings to avoid setting on every mousemove */ + /** Optional cached view settings to avoid setting on every mouse-move. */ PEData data; } BrushEdit; diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c index 2c7b5c0de6a..601a8385a24 100644 --- a/source/blender/editors/physics/particle_edit_undo.c +++ b/source/blender/editors/physics/particle_edit_undo.c @@ -128,10 +128,7 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit) if (edit->points) { MEM_freeN(edit->points); } - if (edit->mirror_cache) { - MEM_freeN(edit->mirror_cache); - edit->mirror_cache = NULL; - } + MEM_SAFE_FREE(edit->mirror_cache); edit->points = MEM_dupallocN(undo->points); edit->totpoint = undo->totpoint; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index d3307ebf274..749010a5ba3 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -768,7 +768,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ - ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out); + ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA16F, err_out); DRW_opengl_context_disable(); if (!ofs) { diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index fe1e850dcba..95351de45f0 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -47,6 +47,7 @@ #include "DNA_collection_types.h" #include "DNA_light_types.h" #include "DNA_material_types.h" +#include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -265,6 +266,11 @@ static const char *preview_collection_name(const ePreviewType pr_type) } } +static bool render_engine_supports_ray_visibility(const Scene *sce) +{ + return !STREQ(sce->r.engine, RE_engine_id_BLENDER_EEVEE); +} + static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type) { /* Set appropriate layer as visible. */ @@ -273,37 +279,68 @@ static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePr for (lc = lc->layer_collections.first; lc; lc = lc->next) { if (STREQ(lc->collection->id.name + 2, collection_name)) { - lc->collection->flag &= ~COLLECTION_RESTRICT_RENDER; + lc->collection->flag &= ~COLLECTION_HIDE_RENDER; } else { - lc->collection->flag |= COLLECTION_RESTRICT_RENDER; + lc->collection->flag |= COLLECTION_HIDE_RENDER; } } } -static void switch_preview_floor_visibility(ViewLayer *view_layer, +static const char *preview_floor_material_name(const Scene *scene, + const ePreviewRenderMethod pr_method) +{ + if (pr_method == PR_ICON_RENDER && render_engine_supports_ray_visibility(scene)) { + return "FloorHidden"; + } + return "Floor"; +} + +static void switch_preview_floor_material(Main *pr_main, + Mesh *me, + const Scene *scene, + const ePreviewRenderMethod pr_method) +{ + if (me->totcol == 0) { + return; + } + + const char *material_name = preview_floor_material_name(scene, pr_method); + Material *mat = BLI_findstring(&pr_main->materials, material_name, offsetof(ID, name) + 2); + if (mat) { + me->mat[0] = mat; + } +} + +static void switch_preview_floor_visibility(Main *pr_main, + const Scene *scene, + ViewLayer *view_layer, const ePreviewRenderMethod pr_method) { /* Hide floor for icon renders. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (STREQ(base->object->id.name + 2, "Floor")) { + base->object->visibility_flag &= ~OB_HIDE_RENDER; if (pr_method == PR_ICON_RENDER) { - base->object->restrictflag |= OB_RESTRICT_RENDER; + if (!render_engine_supports_ray_visibility(scene)) { + base->object->visibility_flag |= OB_HIDE_RENDER; + } } - else { - base->object->restrictflag &= ~OB_RESTRICT_RENDER; + if (base->object->type == OB_MESH) { + switch_preview_floor_material(pr_main, base->object->data, scene, pr_method); } } } } -static void set_preview_visibility(Scene *scene, +static void set_preview_visibility(Main *pr_main, + Scene *scene, ViewLayer *view_layer, const ePreviewType pr_type, const ePreviewRenderMethod pr_method) { switch_preview_collection_visibilty(view_layer, pr_type); - switch_preview_floor_visibility(view_layer, pr_method); + switch_preview_floor_visibility(pr_main, scene, view_layer, pr_method); BKE_layer_collection_sync(scene, view_layer); } @@ -357,10 +394,31 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) } } -static World *preview_get_world(Main *pr_main) +static const char *preview_world_name(const Scene *sce, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) +{ + /* When rendering material icons the floor will not be shown in the output. Cycles will use a + * material trick to show the floor in the reflections, but hide the floor for camera rays. For + * Eevee we use a transparent world that has a projected grid. + * + * In the future when Eevee supports vulkan raytracing we can re-evaluate and perhaps remove this + * approximation. + */ + if (id_type == ID_MA && pr_method == PR_ICON_RENDER && + !render_engine_supports_ray_visibility(sce)) { + return "WorldFloor"; + } + return "World"; +} + +static World *preview_get_world(Main *pr_main, + const Scene *sce, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) { World *result = NULL; - const char *world_name = "World"; + const char *world_name = preview_world_name(sce, id_type, pr_method); result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2); /* No world found return first world. */ @@ -380,9 +438,13 @@ static void preview_sync_exposure(World *dst, const World *src) dst->range = src->range; } -static World *preview_prepare_world(Main *pr_main, const World *world) +static World *preview_prepare_world(Main *pr_main, + const Scene *sce, + const World *world, + const ID_Type id_type, + const ePreviewRenderMethod pr_method) { - World *result = preview_get_world(pr_main); + World *result = preview_get_world(pr_main, sce, id_type, pr_method); if (world) { preview_sync_exposure(result, world); } @@ -436,7 +498,7 @@ static Scene *preview_prepare_scene( sce->r.cfra = scene->r.cfra; /* Setup the world. */ - sce->world = preview_prepare_world(pr_main, scene->world); + sce->world = preview_prepare_world(pr_main, sce, scene->world, id_type, sp->pr_method); if (id_type == ID_TE) { /* Texture is not actually rendered with engine, just set dummy value. */ @@ -458,7 +520,7 @@ static Scene *preview_prepare_scene( /* Use current scene world to light sphere. */ sce->world = preview_get_localized_world(sp, scene->world); } - else if (sce->world) { + else if (sce->world && sp->pr_method != PR_ICON_RENDER) { /* Use a default world color. Using the current * scene world can be slow if it has big textures. */ sce->world->use_nodes = false; @@ -472,7 +534,7 @@ static Scene *preview_prepare_scene( sp->pr_main == G_pr_main_grease_pencil) ? MA_SPHERE_A : mat->pr_type; - set_preview_visibility(sce, view_layer, preview_type, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, preview_type, sp->pr_method); if (sp->pr_method != PR_ICON_RENDER) { if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) { @@ -536,7 +598,7 @@ static Scene *preview_prepare_scene( BLI_addtail(&pr_main->lights, la); } - set_preview_visibility(sce, view_layer, MA_LAMP, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, MA_LAMP, sp->pr_method); if (sce->world) { /* Only use lighting from the light. */ @@ -571,7 +633,7 @@ static Scene *preview_prepare_scene( BLI_addtail(&pr_main->worlds, wrld); } - set_preview_visibility(sce, view_layer, MA_SKY, sp->pr_method); + set_preview_visibility(pr_main, sce, view_layer, MA_SKY, sp->pr_method); sce->world = wrld; if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) { diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index fb9d11feb63..8bc2281db73 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> +#include "DNA_cachefile_types.h" #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" @@ -63,7 +64,9 @@ #include <stdio.h> -/***************************** Render Engines ********************************/ +/* -------------------------------------------------------------------- */ +/** \name Render Engines + * \{ */ /* Update 3D viewport render or draw engine on changes to the scene or view settings. */ void ED_render_view3d_update(Depsgraph *depsgraph, @@ -204,6 +207,19 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) ntreeCompositUpdateRLayers(scene->nodetree); } } + + /* Update #CacheFiles to ensure that procedurals are properly taken into account. */ + LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) { + /* Only update cache-files which are set to use a render procedural. + * We do not use #BKE_cachefile_uses_render_procedural here as we need to update regardless of + * the current engine or its settings. */ + if (cachefile->use_render_procedural) { + DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE); + /* Rebuild relations so that modifiers are reconnected to or disconnected from the + * cache-file. */ + DEG_relations_tag_update(bmain); + } + } } void ED_render_view_layer_changed(Main *bmain, bScreen *screen) @@ -213,10 +229,16 @@ void ED_render_view_layer_changed(Main *bmain, bScreen *screen) } } -/***************************** Updates *********************************** - * ED_render_id_flush_update gets called from DEG_id_tag_update, to do * - * editor level updates when the ID changes. when these ID blocks are in * - * the dependency graph, we can get rid of the manual dependency checks. */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Updates + * + * #ED_render_id_flush_update gets called from #DEG_id_tag_update, + * to do editor level updates when the ID changes. + * When these ID blocks are in the dependency graph, + * we can get rid of the manual dependency checks. + * \{ */ static void material_changed(Main *UNUSED(bmain), Material *ma) { @@ -322,3 +344,5 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id) break; } } + +/** \} */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index c351ade9954..e08a4e946f6 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -834,9 +834,8 @@ void ED_area_status_text(ScrArea *area, const char *str) BLI_strncpy(region->headerstr, str, UI_MAX_DRAW_STR); BLI_str_rstrip(region->headerstr); } - else if (region->headerstr) { - MEM_freeN(region->headerstr); - region->headerstr = NULL; + else { + MEM_SAFE_FREE(region->headerstr); } ED_region_tag_redraw(region); } @@ -859,9 +858,8 @@ void ED_workspace_status_text(bContext *C, const char *str) } BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR); } - else if (workspace->status_text) { - MEM_freeN(workspace->status_text); - workspace->status_text = NULL; + else { + MEM_SAFE_FREE(workspace->status_text); } /* Redraw status bar. */ diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 8123d8bb104..b0181de96a0 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -113,7 +113,7 @@ const char *screen_context_dir[] = { "active_editable_fcurve", "selected_editable_keyframes", "ui_list", - "asset_library", + "asset_library_ref", NULL, }; @@ -1031,7 +1031,7 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe { WorkSpace *workspace = CTX_wm_workspace(C); CTX_data_pointer_set( - result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library); + result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library_ref); return CTX_RESULT_OK; } @@ -1118,7 +1118,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves); register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); - register_context_function("asset_library", screen_ctx_asset_library); + register_context_function("asset_library_ref", screen_ctx_asset_library); register_context_function("ui_list", screen_ctx_ui_list); } diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index dca464bbf22..ab50e327de3 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -451,7 +451,7 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y) void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect) { char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, GPU_RGBA8, err_out); GPU_offscreen_bind(offscreen, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 2a81fcfde8f..1c068fdd6e4 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -230,7 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect) void screen_data_copy(bScreen *to, bScreen *from) { /* free contents of 'to', is from blenkernel screen.c */ - BKE_screen_free(to); + BKE_screen_free_data(to); to->flag = from->flag; @@ -722,10 +722,7 @@ void ED_region_exit(bContext *C, ARegion *region) WM_event_modal_handler_region_replace(win, region, NULL); WM_draw_region_free(region, true); - if (region->headerstr) { - MEM_freeN(region->headerstr); - region->headerstr = NULL; - } + MEM_SAFE_FREE(region->headerstr); if (region->regiontimer) { WM_event_remove_timer(wm, win, region->regiontimer); @@ -1653,10 +1650,7 @@ void ED_refresh_viewport_fps(bContext *C) } else { /* playback stopped or shouldn't be running */ - if (scene->fps_info) { - MEM_freeN(scene->fps_info); - } - scene->fps_info = NULL; + MEM_SAFE_FREE(scene->fps_info); } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 8d7d742e44b..cff3ecfbbd3 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -381,7 +381,7 @@ bool ED_operator_console_active(bContext *C) static bool ed_object_hidden(const Object *ob) { /* if hidden but in edit mode, we still display, can happen with animation */ - return ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)); + return ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)); } bool ED_operator_object_active(bContext *C) @@ -403,7 +403,7 @@ bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob) } if (ed_object_hidden(ob)) { - CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden obect"); + CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden object"); return false; } @@ -1023,10 +1023,7 @@ AZone *ED_area_azones_update(ScrArea *area, const int xy[2]) static void actionzone_exit(wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); G.moving &= ~G_TRANSFORM_WM; } @@ -1308,10 +1305,7 @@ static bool area_swap_init(wmOperator *op, const wmEvent *event) static void area_swap_exit(bContext *C, wmOperator *op) { WM_cursor_modal_restore(CTX_wm_window(C)); - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static void area_swap_cancel(bContext *C, wmOperator *op) @@ -1892,10 +1886,7 @@ static void area_move_apply(bContext *C, wmOperator *op) static void area_move_exit(bContext *C, wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); /* this makes sure aligned edges will result in aligned grabbing */ BKE_screen_remove_double_scrverts(CTX_wm_screen(C)); @@ -2906,7 +2897,7 @@ static void areas_do_frame_follow(bContext *C, bool middle) LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { /* do follow here if editor type supports it */ - if ((screen_ctx->redraws_flag & TIME_FOLLOW)) { + if (screen_ctx->redraws_flag & TIME_FOLLOW) { if ((region->regiontype == RGN_TYPE_WINDOW && ELEM(area->spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) || (area->spacetype == SPACE_CLIP && region->regiontype == RGN_TYPE_PREVIEW)) { @@ -3057,8 +3048,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) float cfra = (float)(CFRA); /* init binarytree-list for getting keyframes */ - DLRBT_Tree keys; - BLI_dlrbTree_init(&keys); + struct AnimKeylist *keylist = ED_keylist_create(); /* seed up dummy dopesheet context with flags to perform necessary filtering */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { @@ -3067,14 +3057,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* populate tree with keyframe nodes */ - scene_to_keylist(&ads, scene, &keys, 0); + scene_to_keylist(&ads, scene, keylist, 0); if (ob) { - ob_to_keylist(&ads, ob, &keys, 0); + ob_to_keylist(&ads, ob, keylist, 0); if (ob->type == OB_GPENCIL) { const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY); - gpencil_to_keylist(&ads, ob->data, &keys, active); + gpencil_to_keylist(&ads, ob->data, keylist, active); } } @@ -3082,17 +3072,17 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) Mask *mask = CTX_data_edit_mask(C); if (mask) { MaskLayer *masklay = BKE_mask_layer_active(mask); - mask_to_keylist(&ads, masklay, &keys); + mask_to_keylist(&ads, masklay, keylist); } } /* find matching keyframe in the right direction */ - ActKeyColumn *ak; + const ActKeyColumn *ak; if (next) { - ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra); + ak = ED_keylist_find_next(keylist, cfra); } else { - ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra); + ak = ED_keylist_find_prev(keylist, cfra); } while ((ak != NULL) && (done == false)) { @@ -3113,7 +3103,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) } /* free temp stuff */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); /* any success? */ if (done == false) { @@ -4488,10 +4478,8 @@ static bool match_region_with_redraws(const ScrArea *area, return false; } -static void screen_animation_region_tag_redraw(ScrArea *area, - ARegion *region, - const Scene *scene, - eScreen_Redraws_Flag redraws) +static void screen_animation_region_tag_redraw( + bContext *C, ScrArea *area, ARegion *region, const Scene *scene, eScreen_Redraws_Flag redraws) { /* Do follow time here if editor type supports it */ if ((redraws & TIME_FOLLOW) && @@ -4515,10 +4503,29 @@ static void screen_animation_region_tag_redraw(ScrArea *area, * We do need to redraw when this area is in full screen as no other areas * will be tagged for redrawing. */ if (region->regiontype == RGN_TYPE_WINDOW && !area->full) { - if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) { + if (ELEM(area->spacetype, SPACE_NLA, SPACE_ACTION)) { return; } + /* Drivers Editor needs a full redraw on playback for graph_draw_driver_debug(). + * This will make it slower than regular graph editor during playback, but drawing this in + * graph_main_region_draw_overlay() is not feasible because it requires animation filtering + * which has significant overhead which needs to be avoided in the overlay which is redrawn on + * every UI interaction. */ + if (area->spacetype == SPACE_GRAPH) { + const SpaceGraph *sipo = area->spacedata.first; + if (sipo->mode != SIPO_MODE_DRIVERS) { + return; + } + bAnimContext ac; + if (ANIM_animdata_get_context(C, &ac) == false) { + return; + } + if (ac.datatype != ANIMCONT_DRIVERS) { + return; + } + } + if (area->spacetype == SPACE_SEQ) { const SpaceSeq *sseq = area->spacedata.first; if (!ED_space_sequencer_has_playback_animation(sseq, scene)) { @@ -4712,7 +4719,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con } if (redraw) { - screen_animation_region_tag_redraw(area, region, scene, sad->redraws); + screen_animation_region_tag_redraw(C, area, region, scene, sad->redraws); } } } @@ -5695,10 +5702,7 @@ static void keymap_modal_set(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "SCREEN_OT_area_move"); } -static bool blend_file_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool blend_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { if (drag->icon == ICON_FILE_BLEND) { @@ -5728,8 +5732,9 @@ void ED_keymap_screen(wmKeyConfig *keyconf) /* dropbox for entire window */ ListBase *lb = WM_dropboxmap_find("Window", 0, 0); - WM_dropbox_add(lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL); - WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL); + WM_dropbox_add( + lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL, NULL); keymap_modal_set(keyconf); } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 23b90171a1d..a35e248a78c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -837,10 +837,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) || renew_maxmask) { - if (cache->tex_mask) { - MEM_freeN(cache->tex_mask); - cache->tex_mask = NULL; - } + MEM_SAFE_FREE(cache->tex_mask); brush_painter_2d_tex_mapping(s, tile->canvas, @@ -862,10 +859,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, } /* curve mask can only change if the size changes */ - if (cache->curve_mask) { - MEM_freeN(cache->curve_mask); - cache->curve_mask = NULL; - } + MEM_SAFE_FREE(cache->curve_mask); cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index a8ad6ab1b74..a58b1947b0c 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -4512,7 +4512,7 @@ static void project_paint_begin(const bContext *C, ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV)); - /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + // printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { reset_threads = true; @@ -5194,8 +5194,8 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena"); } - /* printf("brush bounds %d %d %d %d\n", - * bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ + // printf("brush bounds %d %d %d %d\n", + // bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 83388c1aef2..7bde864e73f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -6560,10 +6560,7 @@ static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) Brush *brush = BKE_paint_brush(&sd->paint); const int radius = BKE_brush_size_get(scene, brush); - if (ss->texcache) { - MEM_freeN(ss->texcache); - ss->texcache = NULL; - } + MEM_SAFE_FREE(ss->texcache); if (ss->tex_pool) { BKE_image_pool_free(ss->tex_pool); @@ -7887,6 +7884,9 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f sculpt_update_cache_invariants(C, sd, ss, op, mouse); + SculptCursorGeometryInfo sgi; + SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); + SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); return true; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 87e0ea7f6a9..ae6dcbdbff4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -94,15 +94,9 @@ void SCULPT_pbvh_clear(Object *ob) ss->pbvh = NULL; } - if (ss->pmap) { - MEM_freeN(ss->pmap); - ss->pmap = NULL; - } + MEM_SAFE_FREE(ss->pmap); - if (ss->pmap_mem) { - MEM_freeN(ss->pmap_mem); - ss->pmap_mem = NULL; - } + MEM_SAFE_FREE(ss->pmap_mem); BKE_object_free_derived_caches(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index eabbfe43e03..38165b7622f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -88,7 +88,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); /* Do not modify corner vertices. */ - if (neighbor_count <= 2) { + if (neighbor_count <= 2 && is_boundary) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 771e0e1e47b..e5ca5e4defd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -190,7 +190,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, /* This is supposed to happen only if "Pin Edges" is on, * since we have initialization on stroke start. * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) { + if (sculptdata->uv[i].flag & MARK_BOUNDARY) { continue; } @@ -268,7 +268,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, /* This is supposed to happen only if "Pin Edges" is on, * since we have initialization on stroke start. * If ever uv brushes get their own mode we should check for toolsettings option too. */ - if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) { + if (sculptdata->uv[i].flag & MARK_BOUNDARY) { continue; } diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index d69c7ab8d48..717d87c4972 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -599,16 +599,13 @@ void ED_animedit_unlink_action( id_fake_user_clear(&act->id); } - /* If in Tweak Mode, don't unlink. Instead, this - * becomes a shortcut to exit Tweak Mode instead - */ + /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */ if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) { - /* Exit Tweak Mode */ BKE_nla_tweakmode_exit(adt); - /* Flush this to the Action Editor (if that's where this change was initiated) */ - if (area->spacetype == SPACE_ACTION) { - actedit_change_action(C, NULL); + Scene *scene = CTX_data_scene(C); + if (scene != NULL) { + scene->flag &= ~SCE_NLA_EDIT_ON; } } else { @@ -660,6 +657,9 @@ static int action_unlink_exec(bContext *C, wmOperator *op) ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports, force_delete); } + /* Unlink is also abused to exit NLA tweak mode. */ + WM_main_add_notifier(NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index ce07b9c5fad..a3bdcd2adf5 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -144,12 +144,14 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region uchar col1[4], col2[4]; uchar col1a[4], col2a[4]; uchar col1b[4], col2b[4]; + uchar col_summary[4]; const bool show_group_colors = U.animation_flag & USER_ANIM_SHOW_CHANNEL_GROUP_COLORS; /* get theme colors */ UI_GetThemeColor4ubv(TH_SHADE2, col2); UI_GetThemeColor4ubv(TH_HILITE, col1); + UI_GetThemeColor4ubv(TH_ANIM_ACTIVE, col_summary); UI_GetThemeColor4ubv(TH_GROUP, col2a); UI_GetThemeColor4ubv(TH_GROUP_ACTIVE, col1a); @@ -244,7 +246,10 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else if (ac->datatype == ANIMCONT_GPENCIL) { uchar *color; uchar gpl_col[4]; - if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) { + if (ale->type == ANIMTYPE_SUMMARY) { + color = col_summary; + } + else if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) { bGPDlayer *gpl = (bGPDlayer *)ale->data; rgb_float_to_uchar(gpl_col, gpl->color); gpl_col[3] = col1[3]; @@ -265,7 +270,13 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else if (ac->datatype == ANIMCONT_MASK) { /* TODO: this is a copy of gpencil. */ /* frames less than one get less saturated background */ - uchar *color = sel ? col1 : col2; + uchar *color; + if (ale->type == ANIMTYPE_SUMMARY) { + color = col_summary; + } + else { + color = sel ? col1 : col2; + } immUniformColor4ubv(color); immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax); @@ -302,6 +313,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region ymax = ACHANNEL_FIRST_TOP(ac); + struct AnimKeylistDrawList *draw_list = ED_keylist_draw_list_create(); + for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac)) { float ymin = ymax - ACHANNEL_HEIGHT(ac); float ycenter = (ymin + ymax) / 2.0f; @@ -316,34 +329,41 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_ALL: - draw_summary_channel(v2d, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_summary_channel(draw_list, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_SCE: - draw_scene_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_scene_channel( + draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_OB: - draw_object_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_object_channel( + draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_ACT: - draw_action_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_action_channel( + draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_GROUP: - draw_agroup_channel(v2d, adt, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_agroup_channel(draw_list, adt, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_FCURVE: - draw_fcurve_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); + draw_fcurve_channel( + draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag); break; case ALE_GPFRAME: - draw_gpl_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_gpl_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag); break; case ALE_MASKLAY: - draw_masklay_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag); + draw_masklay_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag); break; } } } } + ED_keylist_draw_list_flush(draw_list, v2d); + ED_keylist_draw_list_free(draw_list); + /* free temporary channels used for drawing */ ANIM_animdata_freelist(&anim_data); } diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 59d2063ea84..9dcfc626a50 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -93,7 +93,7 @@ static bAnimListElem *actkeys_find_list_element_at_position(bAnimContext *ac, } static void actkeys_list_element_to_keylist(bAnimContext *ac, - DLRBT_Tree *anim_keys, + struct AnimKeylist *keylist, bAnimListElem *ale) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -107,44 +107,44 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac, switch (ale->datatype) { case ALE_SCE: { Scene *scene = (Scene *)ale->key_data; - scene_to_keylist(ads, scene, anim_keys, 0); + scene_to_keylist(ads, scene, keylist, 0); break; } case ALE_OB: { Object *ob = (Object *)ale->key_data; - ob_to_keylist(ads, ob, anim_keys, 0); + ob_to_keylist(ads, ob, keylist, 0); break; } case ALE_ACT: { bAction *act = (bAction *)ale->key_data; - action_to_keylist(adt, act, anim_keys, 0); + action_to_keylist(adt, act, keylist, 0); break; } case ALE_FCURVE: { FCurve *fcu = (FCurve *)ale->key_data; - fcurve_to_keylist(adt, fcu, anim_keys, 0); + fcurve_to_keylist(adt, fcu, keylist, 0); break; } } } else if (ale->type == ANIMTYPE_SUMMARY) { /* dopesheet summary covers everything */ - summary_to_keylist(ac, anim_keys, 0); + summary_to_keylist(ac, keylist, 0); } else if (ale->type == ANIMTYPE_GROUP) { /* TODO: why don't we just give groups key_data too? */ bActionGroup *agrp = (bActionGroup *)ale->data; - agroup_to_keylist(adt, agrp, anim_keys, 0); + agroup_to_keylist(adt, agrp, keylist, 0); } else if (ale->type == ANIMTYPE_GPLAYER) { /* TODO: why don't we just give gplayers key_data too? */ bGPDlayer *gpl = (bGPDlayer *)ale->data; - gpl_to_keylist(ads, gpl, anim_keys); + gpl_to_keylist(ads, gpl, keylist); } else if (ale->type == ANIMTYPE_MASKLAYER) { /* TODO: why don't we just give masklayers key_data too? */ MaskLayer *masklay = (MaskLayer *)ale->data; - mask_to_keylist(ads, masklay, anim_keys); + mask_to_keylist(ads, masklay, keylist); } } @@ -160,9 +160,8 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, View2D *v2d = &ac->region->v2d; - DLRBT_Tree anim_keys; - BLI_dlrbTree_init(&anim_keys); - actkeys_list_element_to_keylist(ac, &anim_keys, ale); + struct AnimKeylist *keylist = ED_keylist_create(); + actkeys_list_element_to_keylist(ac, keylist, ale); AnimData *adt = ANIM_nla_mapping_get(ac, ale); @@ -171,25 +170,23 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, /* half-size (for either side), but rounded up to nearest int (for easier targeting) */ key_hsize = roundf(key_hsize / 2.0f); - float xmin = UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize); - float xmax = UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize); - - for (ActKeyColumn *ak = anim_keys.root; ak; ak = (ak->cfra < xmin) ? ak->right : ak->left) { - if (IN_RANGE(ak->cfra, xmin, xmax)) { - /* set the frame to use, and apply inverse-correction for NLA-mapping - * so that the frame will get selected by the selection functions without - * requiring to map each frame once again... - */ - *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); - *r_frame = ak->cfra; - *r_found = true; - *r_is_selected = (ak->sel & SELECT) != 0; - break; - } + const Range2f range = {UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize), + UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize)}; + const ActKeyColumn *ak = ED_keylist_find_any_between(keylist, range); + if (ak) { + + /* set the frame to use, and apply inverse-correction for NLA-mapping + * so that the frame will get selected by the selection functions without + * requiring to map each frame once again... + */ + *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); + *r_frame = ak->cfra; + *r_found = true; + *r_is_selected = (ak->sel & SELECT) != 0; } /* cleanup temporary lists */ - BLI_dlrbTree_free(&anim_keys); + ED_keylist_free(keylist); } static void actkeys_find_key_at_position(bAnimContext *ac, diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 57a7fe894b0..b04291b7ab4 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -811,6 +811,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) break; case NC_ANIMATION: switch (wmn->data) { + case ND_NLA_ACTCHANGE: + ED_area_tag_redraw(area); + break; case ND_KEYFRAME: if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) { ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 67b4fd61d38..834ef847069 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -1037,6 +1037,7 @@ static void prefetch_freejob(void *pjv) if (clip_local != NULL) { BKE_libblock_free_datablock(&clip_local->id, 0); BKE_libblock_free_data(&clip_local->id, false); + BLI_assert(!clip_local->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(clip_local); } diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 7194e78e940..23dd290e13f 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -492,7 +492,7 @@ static bool mask_has_selection(const bContext *C) } LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { - if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index aef3385f2dc..e2fbb4a5a59 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -605,10 +605,7 @@ static int /*eContextResult*/ clip_context(const bContext *C, } /* dropboxes */ -static bool clip_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool clip_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -639,7 +636,7 @@ static void clip_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Clip", SPACE_CLIP, 0); - WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL); + WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL); } static void clip_refresh(const bContext *C, ScrArea *area) diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c index 96504651e44..58f9b307ef9 100644 --- a/source/blender/editors/space_clip/tracking_ops_solve.c +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -299,10 +299,7 @@ static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op)) track->flag &= ~TRACK_HAS_BUNDLE; } - if (reconstruction->cameras != NULL) { - MEM_freeN(reconstruction->cameras); - reconstruction->cameras = NULL; - } + MEM_SAFE_FREE(reconstruction->cameras); reconstruction->camnr = 0; reconstruction->flag &= ~TRACKING_RECONSTRUCTED; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3029eed1017..47d15efb6ca 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -158,10 +158,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region /* ************* dropboxes ************* */ -static bool id_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(tooltip)) +static bool id_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_get_local_ID(drag, 0) != NULL; } @@ -176,10 +173,7 @@ static void id_drop_copy(wmDrag *drag, wmDropBox *drop) MEM_freeN(text); } -static bool path_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(tooltip)) +static bool path_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_PATH); } @@ -196,8 +190,8 @@ static void console_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL); - WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL); + WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 37a56816677..9a46579780e 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -82,6 +82,9 @@ void ED_file_path_button(bScreen *screen, PointerRNA params_rna_ptr; uiBut *but; + BLI_assert_msg(params != NULL, + "File select parameters not set. The caller is expected to check this."); + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr); /* callbacks for operator check functions */ @@ -1103,7 +1106,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } /* Check if the library exists. */ - if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) || + if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) || filelist_is_dir(sfile->files, asset_params->base_params.dir)) { return false; } diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 616e7fe51db..2f1acd2ca4d 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -546,6 +546,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool fill = RNA_boolean_get(op->ptr, "fill"); const bool do_diropen = RNA_boolean_get(op->ptr, "open"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected"); + /* Used so right mouse clicks can do both, activate and spawn the context menu. */ + const bool pass_through = RNA_boolean_get(op->ptr, "pass_through"); if (region->regiontype != RGN_TYPE_WINDOW) { return OPERATOR_CANCELLED; @@ -563,8 +566,13 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) int numfiles = filelist_files_ensure(sfile->files); if ((idx >= 0) && (idx < numfiles)) { + const bool is_selected = filelist_entry_select_index_get(sfile->files, idx, CHECK_ALL) & + FILE_SEL_SELECTED; + if (only_activate_if_selected && is_selected) { + /* Don't deselect other items. */ + } /* single select, deselect all selected first */ - if (!extend) { + else if (!extend) { file_select_deselect_all(sfile, FILE_SEL_SELECTED); } } @@ -593,7 +601,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - return OPERATOR_FINISHED; + return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED; } void FILE_OT_select(wmOperatorType *ot) @@ -628,6 +636,20 @@ void FILE_OT_select(wmOperatorType *ot) "Deselect On Nothing", "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "only_activate_if_selected", + false, + "Only Activate if Selected", + "Do not change selection if the item under the cursor is already " + "selected, only activate it"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "pass_through", + false, + "Pass Through", + "Even on successful execution, pass the event on so other operators can " + "execute on it as well"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } /** \} */ @@ -1366,7 +1388,9 @@ int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my) FileSelectParams *params; int numfiles, origfile; - if (sfile == NULL || sfile->files == NULL) { + /* In case blender starts where the mouse is over a File browser, + * this operator can be invoked when the `sfile` or `sfile->layout` isn't initialized yet. */ + if (sfile == NULL || sfile->files == NULL || sfile->layout == NULL) { return 0; } @@ -2516,7 +2540,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN /* don't do for now because it selects entire text instead of * placing cursor at the end */ - /* UI_textbutton_activate_but(C, but); */ + // UI_textbutton_activate_but(C, but); } #if defined(WIN32) else if (!can_create_dir(params->dir)) { @@ -2750,20 +2774,6 @@ static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool requ } } -static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) -{ - ScrArea *area = CTX_wm_area(C); - SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); - FileSelectParams *params = ED_fileselect_get_active_params(sfile); - - if (params) { - file_rename_state_activate(sfile, params->active_file, true); - ED_area_tag_redraw(area); - } - - return OPERATOR_FINISHED; -} - static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *area = CTX_wm_area(C); @@ -2771,7 +2781,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - file_rename_state_activate(sfile, params->highlight_file, false); + file_rename_state_activate(sfile, params->active_file, false); ED_area_tag_redraw(area); } @@ -2786,7 +2796,6 @@ void FILE_OT_rename(struct wmOperatorType *ot) ot->idname = "FILE_OT_rename"; /* api callbacks */ - ot->invoke = file_rename_invoke; ot->exec = file_rename_exec; /* File browsing only operator (not asset browsing). */ ot->poll = ED_operator_file_browsing_active; @@ -2830,20 +2839,10 @@ static bool file_delete_single(const FileSelectParams *params, FileDirEntry *file, const char **r_error_message) { - if (file->typeflag & FILE_TYPE_ASSET) { - ID *id = filelist_file_get_id(file); - if (!id) { - *r_error_message = "File is not a local data-block asset."; - return false; - } - ED_asset_clear_id(id); - } - else { - char str[FILE_MAX]; - BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); - if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { - return false; - } + char str[FILE_MAX]; + BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath); + if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) { + return false; } return true; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 630c9aed157..f8ae4be9471 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -379,12 +379,13 @@ enum { FLF_ASSETS_ONLY = 1 << 4, }; +struct FileListReadJob; typedef struct FileList { FileDirEntryArr filelist; eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ - AssetLibraryReference *asset_library; + AssetLibraryReference *asset_library_ref; short flags; @@ -415,8 +416,7 @@ typedef struct FileList { bool (*check_dir_fn)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ - void (*read_job_fn)( - Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *); + void (*read_job_fn)(struct FileListReadJob *, short *, short *, float *); /* Filter an entry of current filelist. */ bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *); @@ -459,34 +459,22 @@ enum { static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX]; -static void filelist_readjob_main(Main *current_main, - FileList *filelist, - const char *main_name, +static void filelist_readjob_main(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_lib(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_lib(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_dir(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_dir(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); -static void filelist_readjob_main_assets(Main *current_main, - FileList *filelist, - const char *main_name, + float *progress); +static void filelist_readjob_main_assets(struct FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock); + float *progress); /* helper, could probably go in BKE actually? */ static int groupname_to_code(const char *group); @@ -1065,28 +1053,28 @@ static bool filelist_compare_asset_libraries(const AssetLibraryReference *librar } /** - * \param asset_library: May be NULL to unset the library. + * \param asset_library_ref: May be NULL to unset the library. */ -void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library) +void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref) { /* Unset if needed. */ - if (!asset_library) { - if (filelist->asset_library) { - MEM_SAFE_FREE(filelist->asset_library); + if (!asset_library_ref) { + if (filelist->asset_library_ref) { + MEM_SAFE_FREE(filelist->asset_library_ref); filelist->flags |= FL_FORCE_RESET; } return; } - if (!filelist->asset_library) { - filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library), - "filelist asset library"); - *filelist->asset_library = *asset_library; + if (!filelist->asset_library_ref) { + filelist->asset_library_ref = MEM_mallocN(sizeof(*filelist->asset_library_ref), + "filelist asset library"); + *filelist->asset_library_ref = *asset_library_ref; filelist->flags |= FL_FORCE_RESET; } - else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) { - *filelist->asset_library = *asset_library; + else if (!filelist_compare_asset_libraries(filelist->asset_library_ref, asset_library_ref)) { + *filelist->asset_library_ref = *asset_library_ref; filelist->flags |= FL_FORCE_RESET; } } @@ -1791,7 +1779,7 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } - MEM_SAFE_FREE(filelist->asset_library); + MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); @@ -1867,7 +1855,7 @@ bool filelist_is_dir(struct FileList *filelist, const char *path) */ void filelist_setdir(struct FileList *filelist, char *r_dir) { - const bool allow_invalid = filelist->asset_library != NULL; + const bool allow_invalid = filelist->asset_library_ref != NULL; BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir); @@ -3133,14 +3121,29 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) } #endif +typedef struct FileListReadJob { + ThreadMutex lock; + char main_name[FILE_MAX]; + Main *current_main; + struct FileList *filelist; + + /** Shallow copy of #filelist for thread-safe access. + * + * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist + * into #filelist in a thread-safe way. + * + * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set + * to NULL to avoid double-freeing them. */ + struct FileList *tmp_filelist; +} FileListReadJob; + static void filelist_readjob_do(const bool do_lib, - FileList *filelist, - const char *main_name, + FileListReadJob *job_params, const short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ ListBase entries = {0}; BLI_Stack *todo_dirs; TodoDir *td_dir; @@ -3164,7 +3167,7 @@ static void filelist_readjob_do(const bool do_lib, BLI_strncpy(dir, filelist->filelist.root, sizeof(dir)); BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob)); - BLI_path_normalize_dir(main_name, dir); + BLI_path_normalize_dir(job_params->main_name, dir); td_dir->dir = BLI_strdup(dir); while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { @@ -3199,7 +3202,7 @@ static void filelist_readjob_do(const bool do_lib, if (!nbr_entries) { is_lib = false; nbr_entries = filelist_readjob_list_dir( - subdir, &entries, filter_glob, do_lib, main_name, skip_currpar); + subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } for (entry = entries.first; entry; entry = entry->next) { @@ -3226,7 +3229,7 @@ static void filelist_readjob_do(const bool do_lib, else { /* We have a directory we want to list, add it to todo list! */ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); - BLI_path_normalize_dir(main_name, dir); + BLI_path_normalize_dir(job_params->main_name, dir); td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = recursion_level + 1; td_dir->dir = BLI_strdup(dir); @@ -3236,14 +3239,14 @@ static void filelist_readjob_do(const bool do_lib, } if (nbr_entries) { - BLI_mutex_lock(lock); + BLI_mutex_lock(&job_params->lock); *do_update = true; BLI_movelisttolist(&filelist->filelist.entries, &entries); filelist->filelist.nbr_entries += nbr_entries; - BLI_mutex_unlock(lock); + BLI_mutex_unlock(&job_params->lock); } nbr_done_dirs++; @@ -3261,51 +3264,40 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_free(todo_dirs); } -static void filelist_readjob_dir(Main *UNUSED(current_main), - FileList *filelist, - const char *main_name, +static void filelist_readjob_dir(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { - filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_do(false, job_params, stop, do_update, progress); } -static void filelist_readjob_lib(Main *UNUSED(current_main), - FileList *filelist, - const char *main_name, +static void filelist_readjob_lib(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { - filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_do(true, job_params, stop, do_update, progress); } -static void filelist_readjob_main(Main *current_main, - FileList *filelist, - const char *main_name, +static void filelist_readjob_main(FileListReadJob *job_params, short *stop, short *do_update, - float *progress, - ThreadMutex *lock) + float *progress) { /* TODO! */ - filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock); + filelist_readjob_dir(job_params, stop, do_update, progress); } /** * \warning Acts on main, so NOT thread-safe! */ -static void filelist_readjob_main_assets(Main *current_main, - FileList *filelist, - const char *UNUSED(main_name), +static void filelist_readjob_main_assets(FileListReadJob *job_params, short *UNUSED(stop), short *do_update, - float *UNUSED(progress), - ThreadMutex *UNUSED(lock)) + float *UNUSED(progress)) { + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); @@ -3317,7 +3309,7 @@ static void filelist_readjob_main_assets(Main *current_main, ID *id_iter; int nbr_entries = 0; - FOREACH_MAIN_ID_BEGIN (current_main, id_iter) { + FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) { if (!id_iter->asset_data) { continue; } @@ -3349,15 +3341,6 @@ static void filelist_readjob_main_assets(Main *current_main, } } -typedef struct FileListReadJob { - ThreadMutex lock; - char main_name[FILE_MAX]; - Main *current_main; - struct FileList *filelist; - /** XXX We may use a simpler struct here... just a linked list and root path? */ - struct FileList *tmp_filelist; -} FileListReadJob; - static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress) { FileListReadJob *flrj = flrjv; @@ -3381,17 +3364,11 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->libfiledata = NULL; memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); flrj->tmp_filelist->selection_state = NULL; - flrj->tmp_filelist->asset_library = NULL; + flrj->tmp_filelist->asset_library_ref = NULL; BLI_mutex_unlock(&flrj->lock); - flrj->tmp_filelist->read_job_fn(flrj->current_main, - flrj->tmp_filelist, - flrj->main_name, - stop, - do_update, - progress, - &flrj->lock); + flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress); } static void filelist_readjob_update(void *flrjv) diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 6915e853681..d67cd89200b 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -73,7 +73,7 @@ void filelist_setfilter_options(struct FileList *filelist, const char *filter_search); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, - const struct AssetLibraryReference *asset_library); + const struct AssetLibraryReference *asset_library_ref); void filelist_init_icons(void); void filelist_free_icons(void); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 89142b6669b..4ab7014cf82 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -118,8 +118,8 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), "FileAssetSelectParams"); asset_params->base_params.details_flags = U_default.file_space_data.details_flags; - asset_params->asset_library.type = ASSET_LIBRARY_LOCAL; - asset_params->asset_library.custom_library_index = -1; + asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL; + asset_params->asset_library_ref.custom_library_index = -1; asset_params->import_type = FILE_ASSET_IMPORT_APPEND; } @@ -415,7 +415,7 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) { - AssetLibraryReference *library = &asset_params->asset_library; + AssetLibraryReference *library = &asset_params->asset_library_ref; FileSelectParams *base_params = &asset_params->base_params; bUserAssetLibrary *user_library = NULL; @@ -863,20 +863,8 @@ FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, float file_string_width(const char *str) { const uiStyle *style = UI_style_get(); - float width; - UI_fontstyle_set(&style->widget); - if (style->widget.kerning == 1) { /* for BLF_width */ - BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT); - } - - width = BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); - - if (style->widget.kerning == 1) { - BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT); - } - - return width; + return BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } float file_font_pointsize(void) diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 2d1151c8f4d..776bb0b3bb7 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -969,7 +969,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/"); const uint dir_len = BLI_filelist_dir_contents(name, &dir); for (uint i = 0; i < dir_len; i++) { - if ((dir[i].type & S_IFDIR)) { + if (dir[i].type & S_IFDIR) { const char *dirname = dir[i].relname; if (dirname[0] != '.') { /* Dir names contain a lot of unwanted text. diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 46cc96ba0d4..7deaa2fec60 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -176,10 +176,7 @@ static void file_free(SpaceLink *sl) MEM_SAFE_FREE(sfile->asset_params); MEM_SAFE_FREE(sfile->runtime); - if (sfile->layout) { - MEM_freeN(sfile->layout); - sfile->layout = NULL; - } + MEM_SAFE_FREE(sfile->layout); } /* spacetype; init callback, area size changes, screen set, etc */ @@ -337,11 +334,17 @@ static void file_refresh(const bContext *C, ScrArea *area) sfile->files = filelist_new(params->type); params->highlight_file = -1; /* added this so it opens nicer (ton) */ } + + if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) { + /* Only poses supported as non-experimental right now. */ + params->filter_id = FILTER_ID_AC; + } + filelist_settype(sfile->files, params->type); filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); - filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL); + filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library_ref : NULL); filelist_setfilter_options( sfile->files, (params->flag & FILE_FILTER) != 0, @@ -578,6 +581,16 @@ static void file_main_region_message_subscribe(const wmRegionMessageSubscribePar /* All properties for this space type. */ WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__); } + + /* Experimental Asset Browser features option. */ + { + PointerRNA ptr; + RNA_pointer_create(NULL, &RNA_PreferencesExperimental, &U.experimental, &ptr); + PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_extended_asset_browser"); + + /* All properties for this space type. */ + WM_msg_subscribe_rna(mbus, &ptr, prop, &msg_sub_value_area_tag_refresh, __func__); + } } static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile) @@ -815,10 +828,7 @@ static void file_ui_region_listener(const wmRegionListenerParams *listener_param } } -static bool filepath_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -839,7 +849,7 @@ static void file_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL); + WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL, NULL); } static int file_space_subtype_get(ScrArea *area) @@ -858,18 +868,12 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), EnumPropertyItem **item, int *totitem) { - if (U.experimental.use_asset_browser) { - RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); - } - else { - RNA_enum_items_add_value( - item, totitem, rna_enum_space_file_browse_mode_items, FILE_BROWSE_MODE_FILES); - } + RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items); } static const char *file_context_dir[] = { "active_file", - "asset_library", + "asset_library_ref", "id", NULL, }; @@ -903,14 +907,14 @@ static int /*eContextResult*/ file_context(const bContext *C, CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } - if (CTX_data_equals(member, "asset_library")) { + if (CTX_data_equals(member, "asset_library_ref")) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); if (!asset_params) { return CTX_RESULT_NO_DATA; } CTX_data_pointer_set( - result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library); + result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref); return CTX_RESULT_OK; } if (CTX_data_equals(member, "id")) { diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index ec5f443e2dc..f4c4b6cafcd 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -364,7 +364,7 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel) } block = uiLayoutGetBlock(layout); - /* UI_block_func_handle_set(block, do_graph_region_buttons, NULL); */ + // UI_block_func_handle_set(block, do_graph_region_buttons, NULL); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index dad354ba8ee..29c1452b988 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1289,8 +1289,10 @@ static Image *image_open_single(Main *bmain, } if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) { - if (range->udim_tiles.first && range->offset == 1001) { + if (range->udim_tiles.first) { ima->source = IMA_SRC_TILED; + ImageTile *first_tile = ima->tiles.first; + first_tile->tile_number = range->offset; LISTBASE_FOREACH (LinkData *, node, &range->udim_tiles) { BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL); } @@ -1806,10 +1808,13 @@ static int image_save_options_init(Main *bmain, } /* append UDIM numbering if not present */ - if (ima->source == IMA_SRC_TILED && - (BLI_path_sequence_decode(ima->filepath, NULL, NULL, NULL) != 1001)) { + if (ima->source == IMA_SRC_TILED) { + char udim[6]; + ImageTile *tile = ima->tiles.first; + BLI_snprintf(udim, sizeof(udim), ".%d", tile->tile_number); + int len = strlen(opts->filepath); - STR_CONCAT(opts->filepath, len, ".1001"); + STR_CONCAT(opts->filepath, len, udim); } } @@ -2508,10 +2513,7 @@ static ImageNewData *image_new_init(bContext *C, wmOperator *op) static void image_new_free(wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - op->customdata = NULL; - } + MEM_SAFE_FREE(op->customdata); } static int image_new_exec(bContext *C, wmOperator *op) @@ -3871,9 +3873,9 @@ static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile) /* Acquire ibuf to get the default values. * If the specified tile has no ibuf, try acquiring the main tile instead - * (unless the specified tile already was the main tile). */ + * (unless the specified tile already was the first tile). */ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - if (ibuf == NULL && (tile != NULL) && (tile->tile_number != 1001)) { + if (ibuf == NULL && (tile != NULL) && (tile != ima->tiles.first)) { ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); } @@ -3925,7 +3927,7 @@ static int tile_add_exec(bContext *C, wmOperator *op) Image *ima = CTX_data_edit_image(C); int start_tile = RNA_int_get(op->ptr, "number"); - int end_tile = start_tile + RNA_int_get(op->ptr, "count"); + int end_tile = start_tile + RNA_int_get(op->ptr, "count") - 1; if (start_tile < 1001 || end_tile > IMA_UDIM_MAX) { BKE_report(op->reports, RPT_ERROR, "Invalid UDIM index range was specified"); @@ -3935,26 +3937,30 @@ static int tile_add_exec(bContext *C, wmOperator *op) bool fill_tile = RNA_boolean_get(op->ptr, "fill"); char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0); - bool created_tile = false; - for (int tile_number = start_tile; tile_number < end_tile; tile_number++) { + /* BKE_image_add_tile assumes a pre-sorted list of tiles. */ + BKE_image_sort_tiles(ima); + + ImageTile *last_tile_created = NULL; + for (int tile_number = start_tile; tile_number <= end_tile; tile_number++) { ImageTile *tile = BKE_image_add_tile(ima, tile_number, label); if (tile != NULL) { - ima->active_tile_index = BLI_findindex(&ima->tiles, tile); - if (fill_tile) { do_fill_tile(op->ptr, ima, tile); } - created_tile = true; + last_tile_created = tile; } } MEM_freeN(label); - if (!created_tile) { + if (!last_tile_created) { + BKE_report(op->reports, RPT_WARNING, "No UDIM tiles were created"); return OPERATOR_CANCELLED; } + ima->active_tile_index = BLI_findindex(&ima->tiles, last_tile_created); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); return OPERATOR_FINISHED; } @@ -4043,7 +4049,7 @@ static bool tile_remove_poll(bContext *C) { Image *ima = CTX_data_edit_image(C); - return (ima != NULL && ima->source == IMA_SRC_TILED && ima->active_tile_index != 0); + return (ima != NULL && ima->source == IMA_SRC_TILED && !BLI_listbase_is_single(&ima->tiles)); } static int tile_remove_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index 02546e3e3b3..288b3d94b1d 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -124,7 +124,7 @@ static int image_cmp_frame(const void *a, const void *b) * * udim_tiles may get filled even if the result ultimately is false! */ -static int image_get_udim(char *filepath, ListBase *udim_tiles) +static bool image_get_udim(char *filepath, ListBase *udim_tiles, int *udim_start, int *udim_range) { char filename[FILE_MAX], dirname[FILE_MAXDIR]; BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); @@ -133,12 +133,12 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) char base_head[FILE_MAX], base_tail[FILE_MAX]; int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits); - if (id < 1001 || id >= IMA_UDIM_MAX) { - return 0; + if (id < 1001 || id > IMA_UDIM_MAX) { + return false; } bool is_udim = true; - bool has_primary = false; + int min_udim = IMA_UDIM_MAX + 1; int max_udim = 0; struct direntry *dir; @@ -155,26 +155,27 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles) continue; } - if (id < 1001 || id >= IMA_UDIM_MAX) { + if (id < 1001 || id > IMA_UDIM_MAX) { is_udim = false; break; } - if (id == 1001) { - has_primary = true; - } BLI_addtail(udim_tiles, BLI_genericNodeN(POINTER_FROM_INT(id))); + min_udim = min_ii(min_udim, id); max_udim = max_ii(max_udim, id); } BLI_filelist_free(dir, totfile); - if (is_udim && has_primary) { + if (is_udim && min_udim <= IMA_UDIM_MAX) { char primary_filename[FILE_MAX]; - BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, 1001); + BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, min_udim); BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename); - return max_udim - 1000; + + *udim_start = min_udim; + *udim_range = max_udim - min_udim + 1; + return true; } - return 0; + return false; } /** @@ -185,11 +186,12 @@ static void image_detect_frame_range(ImageFrameRange *range, const bool detect_u { /* UDIM */ if (detect_udim) { - int len_udim = image_get_udim(range->filepath, &range->udim_tiles); + int udim_start, udim_range; + bool result = image_get_udim(range->filepath, &range->udim_tiles, &udim_start, &udim_range); - if (len_udim > 0) { - range->offset = 1001; - range->length = len_udim; + if (result) { + range->offset = udim_start; + range->length = udim_range; return; } } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 5a03b4f6ef0..4107fd619aa 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -253,10 +253,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) } /* dropboxes */ -static bool image_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); if (ED_region_overlap_isect_any_xy(area, &event->x)) { @@ -282,7 +279,7 @@ static void image_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Image", SPACE_IMAGE, 0); - WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL); + WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL, NULL); } /** diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index d7671a372c6..e749e1a7947 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -466,10 +466,7 @@ static void stats_update(Depsgraph *depsgraph, void ED_info_stats_clear(wmWindowManager *wm, ViewLayer *view_layer) { - if (view_layer->stats) { - MEM_freeN(view_layer->stats); - view_layer->stats = NULL; - } + MEM_SAFE_FREE(view_layer->stats); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { ViewLayer *view_layer_test = WM_window_get_active_view_layer(win); @@ -764,6 +761,7 @@ void ED_info_draw_stats( FRAMES, STROKES, POINTS, + LIGHTS, MAX_LABELS_COUNT }; char labels[MAX_LABELS_COUNT][64]; @@ -779,6 +777,7 @@ void ED_info_draw_stats( STRNCPY(labels[FRAMES], IFACE_("Frames")); STRNCPY(labels[STROKES], IFACE_("Strokes")); STRNCPY(labels[POINTS], IFACE_("Points")); + STRNCPY(labels[LIGHTS], IFACE_("Lights")); int longest_label = 0; int i; @@ -832,6 +831,9 @@ void ED_info_draw_stats( stats_row(col1, labels[FACES], col2, stats_fmt.totfacesculpt, stats_fmt.totface, y, height); } } + else if ((ob) && (ob->type == OB_LAMP)) { + stats_row(col1, labels[LIGHTS], col2, stats_fmt.totlampsel, stats_fmt.totlamp, y, height); + } else { stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, NULL, y, height); stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, NULL, y, height); diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 0498964c549..1b87a8c6b9d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -562,7 +562,8 @@ void NLA_OT_action_pushdown(wmOperatorType *ot) static bool nla_action_unlink_poll(bContext *C) { if (ED_operator_nla_active(C)) { - return nla_panel_context(C, NULL, NULL, NULL); + PointerRNA adt_ptr; + return (nla_panel_context(C, &adt_ptr, NULL, NULL) && (adt_ptr.data != NULL)); } /* something failed... */ diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index c96047da0c8..c1b308d213f 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -35,6 +35,7 @@ #include "BLI_blenlib.h" #include "BLI_dlrbTree.h" +#include "BLI_range.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -95,12 +96,16 @@ void nla_action_get_color(AnimData *adt, bAction *act, float color[4]) static void nla_action_draw_keyframes( View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax) { + if (act == NULL) { + return; + } + /* get a list of the keyframes with NLA-scaling applied */ - DLRBT_Tree keys; - BLI_dlrbTree_init(&keys); - action_to_keylist(adt, act, &keys, 0); + struct AnimKeylist *keylist = ED_keylist_create(); + action_to_keylist(adt, act, keylist, 0); - if (ELEM(NULL, act, keys.first)) { + if (ED_keylist_is_empty(keylist)) { + ED_keylist_free(keylist); return; } @@ -122,25 +127,29 @@ static void nla_action_draw_keyframes( /* - draw a rect from the first to the last frame (no extra overlaps for now) * that is slightly stumpier than the track background (hardcoded 2-units here) */ - float f1 = ((ActKeyColumn *)keys.first)->cfra; - float f2 = ((ActKeyColumn *)keys.last)->cfra; - immRectf(pos_id, f1, ymin + 2, f2, ymax - 2); + Range2f frame_range; + ED_keylist_frame_range(keylist, &frame_range); + immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2); immUnbindProgram(); /* Count keys before drawing. */ /* NOTE: It's safe to cast #DLRBT_Tree, as it's designed to degrade down to a #ListBase. */ - uint key_len = BLI_listbase_count((ListBase *)&keys); + const ListBase *keys = ED_keylist_listbase(keylist); + uint key_len = BLI_listbase_count(keys); if (key_len > 0) { format = immVertexFormat(); - pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color_id = GPU_vertformat_attr_add( + KeyframeShaderBindings sh_bindings; + sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + sh_bindings.size_id = GPU_vertformat_attr_add( + format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + sh_bindings.color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint outline_color_id = GPU_vertformat_attr_add( + sh_bindings.outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + sh_bindings.flags_id = GPU_vertformat_attr_add( + format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -151,7 +160,7 @@ static void nla_action_draw_keyframes( /* - disregard the selection status of keyframes so they draw a certain way * - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction */ - LISTBASE_FOREACH (ActKeyColumn *, ak, &keys) { + LISTBASE_FOREACH (const ActKeyColumn *, ak, keys) { draw_keyframe_shape(ak->cfra, y, 6.0f, @@ -159,11 +168,7 @@ static void nla_action_draw_keyframes( ak->key_type, KEYFRAME_SHAPE_FRAME, 1.0f, - pos_id, - size_id, - color_id, - outline_color_id, - flags_id, + &sh_bindings, KEYFRAME_HANDLE_NONE, KEYFRAME_EXTREME_NONE); } @@ -174,7 +179,7 @@ static void nla_action_draw_keyframes( } /* free icons */ - BLI_dlrbTree_free(&keys); + ED_keylist_free(keylist); } /* Strip Markers ------------------------ */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 56efcd8571f..c75b874833a 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -241,9 +241,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo) ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); - /* if we managed to enter tweak-mode on at least one AnimData block, - * set the flag for this in the active scene and send notifiers - */ + /* Clear the tweak-mode flag in the active scene and send notifiers. */ if (ac->scene) { /* clear editing flag */ ac->scene->flag &= ~SCE_NLA_EDIT_ON; diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 95eb1ccc025..0f7a911e3ce 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3596,8 +3596,26 @@ static void std_node_socket_draw( break; } case SOCK_TEXTURE: { - uiTemplateID( - layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + if (text[0] == '\0') { + uiTemplateID(layout, + C, + ptr, + "default_value", + "texture.new", + nullptr, + 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", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); + } + break; } case SOCK_MATERIAL: { diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 6c07c41f451..a1068f29624 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -664,7 +664,8 @@ void NODE_OT_select(wmOperatorType *ot) /* properties */ WM_operator_properties_generic_select(ot); - RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); prop = RNA_def_boolean(ot->srna, "deselect_all", diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index ff848a7bb95..956fb3aa867 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -664,42 +664,29 @@ static void node_main_region_draw(const bContext *C, ARegion *region) /* ************* dropboxes ************* */ -static bool node_group_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_NT); } -static bool node_object_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_object_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_OB); } static bool node_collection_drop_poll(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) + const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_GR); } -static bool node_texture_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_texture_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_TE); } -static bool node_ima_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -708,10 +695,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C), return WM_drag_is_ID_type(drag, ID_IM); } -static bool node_mask_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return WM_drag_is_ID_type(drag, ID_MSK); } @@ -753,32 +737,38 @@ static void node_dropboxes(void) "NODE_OT_add_object", node_object_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_collection", node_collection_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_texture", node_texture_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_group", node_group_drop_poll, node_group_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_file", node_ima_drop_poll, node_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "NODE_OT_add_mask", node_mask_drop_poll, node_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 6538f5709b7..1ec1afe86fc 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -1263,22 +1263,22 @@ static bool collection_flag_poll(bContext *C, bool clear, int flag) static bool collection_enable_poll(bContext *C) { - return collection_flag_poll(C, true, COLLECTION_RESTRICT_VIEWPORT); + return collection_flag_poll(C, true, COLLECTION_HIDE_VIEWPORT); } static bool collection_disable_poll(bContext *C) { - return collection_flag_poll(C, false, COLLECTION_RESTRICT_VIEWPORT); + return collection_flag_poll(C, false, COLLECTION_HIDE_VIEWPORT); } static bool collection_enable_render_poll(bContext *C) { - return collection_flag_poll(C, true, COLLECTION_RESTRICT_RENDER); + return collection_flag_poll(C, true, COLLECTION_HIDE_RENDER); } static bool collection_disable_render_poll(bContext *C) { - return collection_flag_poll(C, false, COLLECTION_RESTRICT_RENDER); + return collection_flag_poll(C, false, COLLECTION_HIDE_RENDER); } static int collection_flag_exec(bContext *C, wmOperator *op) @@ -1288,7 +1288,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); const bool is_render = strstr(op->idname, "render"); const bool clear = strstr(op->idname, "show") || strstr(op->idname, "enable"); - int flag = is_render ? COLLECTION_RESTRICT_RENDER : COLLECTION_RESTRICT_VIEWPORT; + int flag = is_render ? COLLECTION_HIDE_RENDER : COLLECTION_HIDE_VIEWPORT; struct CollectionEditData data = { .scene = scene, .space_outliner = space_outliner, diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 86aab86db10..a82f516b125 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -31,6 +31,7 @@ #include "DNA_space_types.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BLT_translation.h" @@ -316,10 +317,7 @@ static bool allow_parenting_without_modifier_key(SpaceOutliner *space_outliner) } } -static bool parent_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -455,10 +453,7 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot) /* ******************** Parent Clear Operator *********************** */ -static bool parent_clear_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -541,10 +536,7 @@ void OUTLINER_OT_parent_clear(wmOperatorType *ot) /* ******************** Scene Drop Operator *********************** */ -static bool scene_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { /* Ensure item under cursor is valid drop target */ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); @@ -609,10 +601,7 @@ void OUTLINER_OT_scene_drop(wmOperatorType *ot) /* ******************** Material Drop Operator *********************** */ -static bool material_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { /* Ensure item under cursor is valid drop target */ Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA); @@ -833,10 +822,7 @@ static bool datastack_drop_are_types_valid(StackDropData *drop_data) return true; } -static bool datastack_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { if (drag->type != WM_DRAG_DATASTACK) { return false; @@ -873,33 +859,41 @@ static bool datastack_drop_poll(bContext *C, break; } + if (changed) { + ED_region_tag_redraw_no_rebuild(region); + } + + return true; +} + +static char *datastack_drop_tooltip(bContext *UNUSED(C), + wmDrag *drag, + const wmEvent *UNUSED(event), + struct wmDropBox *UNUSED(drop)) +{ + StackDropData *drop_data = drag->poin; switch (drop_data->drop_action) { case DATA_STACK_DROP_REORDER: - *r_tooltip = TIP_("Reorder"); + return BLI_strdup(TIP_("Reorder")); break; case DATA_STACK_DROP_COPY: if (drop_data->pchan_parent) { - *r_tooltip = TIP_("Copy to bone"); + return BLI_strdup(TIP_("Copy to bone")); } else { - *r_tooltip = TIP_("Copy to object"); + return BLI_strdup(TIP_("Copy to object")); } break; case DATA_STACK_DROP_LINK: if (drop_data->pchan_parent) { - *r_tooltip = TIP_("Link all to bone"); + return BLI_strdup(TIP_("Link all to bone")); } else { - *r_tooltip = TIP_("Link all to object"); + return BLI_strdup(TIP_("Link all to object")); } break; } - - if (changed) { - ED_region_tag_redraw_no_rebuild(region); - } - - return true; + return NULL; } static void datastack_drop_link(bContext *C, StackDropData *drop_data) @@ -1110,10 +1104,6 @@ static bool collection_drop_init(bContext *C, if (ID_IS_LINKED(to_collection)) { return false; } - /* Currently this should not be allowed (might be supported in the future though...). */ - if (ID_IS_OVERRIDE_LIBRARY(to_collection)) { - return false; - } /* Get drag datablocks. */ if (drag->type != WM_DRAG_ID) { @@ -1137,6 +1127,11 @@ static bool collection_drop_init(bContext *C, from_collection = NULL; } + /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ + if (from_collection != NULL && ID_IS_OVERRIDE_LIBRARY(from_collection)) { + return false; + } + /* Get collections. */ if (GS(id->name) == ID_GR) { if (id == &to_collection->id) { @@ -1147,6 +1142,12 @@ static bool collection_drop_init(bContext *C, insert_type = TE_INSERT_INTO; } + /* Currently this should not be allowed, cannot edit items in an override of a Collection. */ + if (ID_IS_OVERRIDE_LIBRARY(to_collection) && + !ELEM(insert_type, TE_INSERT_AFTER, TE_INSERT_BEFORE)) { + return false; + } + data->from = from_collection; data->to = to_collection; data->te = te; @@ -1155,10 +1156,7 @@ static bool collection_drop_init(bContext *C, return true; } -static bool collection_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); ARegion *region = CTX_wm_region(C); @@ -1172,45 +1170,20 @@ static bool collection_drop_poll(bContext *C, if (!data.from || event->ctrl) { tselem->flag |= TSE_DRAG_INTO; changed = true; - *r_tooltip = TIP_("Link inside Collection"); } else { switch (data.insert_type) { case TE_INSERT_BEFORE: tselem->flag |= TSE_DRAG_BEFORE; changed = true; - if (te->prev && outliner_is_collection_tree_element(te->prev)) { - *r_tooltip = TIP_("Move between collections"); - } - else { - *r_tooltip = TIP_("Move before collection"); - } break; case TE_INSERT_AFTER: tselem->flag |= TSE_DRAG_AFTER; changed = true; - if (te->next && outliner_is_collection_tree_element(te->next)) { - *r_tooltip = TIP_("Move between collections"); - } - else { - *r_tooltip = TIP_("Move after collection"); - } break; case TE_INSERT_INTO: { tselem->flag |= TSE_DRAG_INTO; changed = true; - - /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" - * for collections. Checking the type of the first ID works fine here since - * all drag IDs are the same type. */ - wmDragID *drag_id = (wmDragID *)drag->ids.first; - const bool is_object = (GS(drag_id->id->name) == ID_OB); - if (is_object) { - *r_tooltip = TIP_("Move inside collection (Ctrl to link, Shift to parent)"); - } - else { - *r_tooltip = TIP_("Move inside collection (Ctrl to link)"); - } break; } } @@ -1226,6 +1199,52 @@ static bool collection_drop_poll(bContext *C, return false; } +static char *collection_drop_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + wmDropBox *UNUSED(drop)) +{ + CollectionDrop data; + if (!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")); + } + switch (data.insert_type) { + case TE_INSERT_BEFORE: + if (te->prev && outliner_is_collection_tree_element(te->prev)) { + return BLI_strdup(TIP_("Move between collections")); + } + else { + return BLI_strdup(TIP_("Move before collection")); + } + break; + case TE_INSERT_AFTER: + if (te->next && outliner_is_collection_tree_element(te->next)) { + return BLI_strdup(TIP_("Move between collections")); + } + else { + return BLI_strdup(TIP_("Move after collection")); + } + break; + case TE_INSERT_INTO: { + + /* Check the type of the drag IDs to avoid the incorrect "Shift to parent" + * for collections. Checking the type of the first ID works fine here since + * all drag IDs are the same type. */ + wmDragID *drag_id = (wmDragID *)drag->ids.first; + const bool is_object = (GS(drag_id->id->name) == ID_OB); + if (is_object) { + return BLI_strdup(TIP_("Move inside collection (Ctrl to link, Shift to parent)")); + } + return BLI_strdup(TIP_("Move inside collection (Ctrl to link)")); + break; + } + } + } + return NULL; +} + static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { Main *bmain = CTX_data_main(C); @@ -1499,10 +1518,16 @@ void outliner_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL); - WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", collection_drop_poll, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL, NULL); + WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL, NULL); + WM_dropbox_add( + lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL, datastack_drop_tooltip); + WM_dropbox_add(lb, + "OUTLINER_OT_collection_drop", + collection_drop_poll, + NULL, + NULL, + collection_drop_tooltip); } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 5be6c69363e..738db28a2b6 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1773,7 +1773,7 @@ static void tree_element_to_path(TreeElement *te, char buf[128], *name; temnext = (TreeElement *)(ld->next->data); - /* tsenext = TREESTORE(temnext); */ /* UNUSED */ + // tsenext = TREESTORE(temnext); /* UNUSED */ nextptr = &temnext->rnaptr; name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index aaa52f6b649..898e66e7a39 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -1682,7 +1682,8 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); + prop = RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean( ot->srna, "extend_range", false, "Extend Range", "Select a range from active element"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 3edb12c5503..d88ae82cc9a 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -92,6 +92,7 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "SEQ_relations.h" #include "SEQ_sequencer.h" #include "outliner_intern.h" @@ -845,8 +846,20 @@ static void id_override_library_create_fn(bContext *C, if (!ID_IS_LINKED(te->store_elem->id)) { break; } + /* If we'd need to override that aren't ID, but it is not overridable, abort. */ + if (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Could not create library override from data-block '%s', one of its parents " + "is not overridable ('%s')", + id_root->name, + te->store_elem->id->name); + return; + } te->store_elem->id->tag |= LIB_TAG_DOIT; } + success = BKE_lib_override_library_create( bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL); } @@ -1269,18 +1282,31 @@ static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), } } -static void sequence_fn(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr) +static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *scene_ptr) { Sequence *seq = (Sequence *)te->directdata; - if (event == OL_DOP_SELECT) { - Scene *scene = (Scene *)scene_ptr; - Editing *ed = SEQ_editing_get(scene, false); - if (BLI_findindex(ed->seqbasep, seq) != -1) { + Scene *scene = (Scene *)scene_ptr; + Editing *ed = SEQ_editing_get(scene, false); + if (BLI_findindex(ed->seqbasep, seq) != -1) { + if (event == OL_DOP_SELECT) { ED_sequencer_select_sequence_single(scene, seq, true); } + else if (event == OL_DOP_DESELECT) { + seq->flag &= ~SELECT; + } + else if (event == OL_DOP_HIDE) { + if (!(seq->flag & SEQ_MUTE)) { + seq->flag |= SEQ_MUTE; + SEQ_relations_invalidate_dependent(scene, seq); + } + } + else if (event == OL_DOP_UNHIDE) { + if (seq->flag & SEQ_MUTE) { + seq->flag &= ~SEQ_MUTE; + SEQ_relations_invalidate_dependent(scene, seq); + } + } } - - (void)tselem; } static void gpencil_layer_fn(int event, @@ -2697,16 +2723,6 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot) /** \name Data Menu Operator * \{ */ -/* XXX: select linked is for RNA structs only. */ -static const EnumPropertyItem prop_data_op_types[] = { - {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, - {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, - {OL_DOP_HIDE, "HIDE", 0, "Hide", ""}, - {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""}, - {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, - {0, NULL, 0, NULL, NULL}, -}; - static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); @@ -2750,6 +2766,8 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); outliner_do_data_operation( space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + ED_undo_push(C, "Sequencer operation"); break; } @@ -2777,6 +2795,42 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/* Dynamically populate an enum of Keying Sets */ +static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) +{ + /* Check for invalid states. */ + if (C == NULL) { + return DummyRNA_DEFAULT_items; + } + + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + if (space_outliner == NULL) { + return DummyRNA_DEFAULT_items; + } + + static const EnumPropertyItem optype_sel_and_hide[] = { + {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, + {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, + {OL_DOP_HIDE, "HIDE", 0, "Hide", ""}, + {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""}, + {0, NULL, 0, NULL, NULL}}; + + 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; + } + + return optype_sel_and_hide; +} + void OUTLINER_OT_data_operation(wmOperatorType *ot) { /* identifiers */ @@ -2790,7 +2844,8 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) ot->flag = 0; - ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", ""); + ot->prop = RNA_def_enum(ot->srna, "type", DummyRNA_DEFAULT_items, 0, "Data Operation", ""); + RNA_def_enum_funcs(ot->prop, outliner_data_op_sets_enum_item_fn); } /** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index b6b3d1841d2..ff8cbdb1a59 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -159,7 +159,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) "set_view_transform", true, "Set View Transform", - "Set appropriate view transform based on media colorspace"); + "Set appropriate view transform based on media color space"); } } @@ -565,10 +565,7 @@ static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) { - if (op->customdata) { - MEM_freeN(op->customdata); - } - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr), @@ -646,15 +643,17 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; + double video_start_offset; + load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); load_data->channel--; if (seq_movie == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } else { if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); } load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); @@ -673,8 +672,10 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; + double video_start_offset; + load_data->channel++; - seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); load_data->channel--; if (seq_movie == NULL) { @@ -682,7 +683,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); } seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); @@ -710,13 +711,13 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) } else { if (!sequencer_add_movie_single_strip(C, op, &load_data)) { + sequencer_add_cancel(C, op); return OPERATOR_CANCELLED; } } - if (op->customdata) { - MEM_freeN(op->customdata); - } + /* Free custom data. */ + sequencer_add_cancel(C, op); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -825,7 +826,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C, RNA_string_get(&itemptr, "name", file_only); BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } @@ -843,7 +844,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, true); - Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f); if (seq == NULL) { BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); return false; @@ -1043,6 +1044,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) load_data.image.len = sequencer_add_image_strip_calculate_length( op, load_data.start_frame, &minframe, &numdigits); if (load_data.image.len == 0) { + sequencer_add_cancel(C, op); return OPERATOR_CANCELLED; } @@ -1065,9 +1067,8 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - if (op->customdata) { - MEM_freeN(op->customdata); - } + /* Free custom data. */ + sequencer_add_cancel(C, op); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 3f8dea8b533..b3c39e2fa6f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -228,9 +228,93 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) } } +typedef struct WaveVizData { + float pos[2]; + float rms_pos; + bool clip; + bool end; +} WaveVizData; + +static int get_section_len(WaveVizData *start, WaveVizData *end) +{ + int len = 0; + while (start != end) { + len++; + if (start->end) { + return len; + } + start++; + } + return len; +} + +static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms) +{ + int strip_len = get_section_len(iter, end); + if (strip_len > 1) { + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, strip_len); + + while (iter != end) { + if (iter->clip) { + immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + } + else if (use_rms) { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); + } + else { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + } + + if (use_rms) { + immVertex2f(pos, iter->pos[0], iter->rms_pos); + } + else { + immVertex2f(pos, iter->pos[0], iter->pos[1]); + } + + if (iter->end) { + /* End of line. */ + iter++; + strip_len = get_section_len(iter, end); + if (strip_len != 0) { + immEnd(); + immUnbindProgram(); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, strip_len); + } + } + else { + iter++; + } + } + immEnd(); + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + } +} + +static float clamp_frame_coord_to_pixel(float frame_coord, + float pixel_frac, + float frames_per_pixel) +{ + float cur_pixel = (frame_coord / frames_per_pixel); + float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac; + if (cur_pixel > new_pixel) { + new_pixel += 1.0f; + } + return new_pixel * frames_per_pixel; +} + /** * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. - * \param stepsize: The width of a pixel. + * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). */ static void draw_seq_waveform_overlay(View2D *v2d, const bContext *C, @@ -241,29 +325,34 @@ static void draw_seq_waveform_overlay(View2D *v2d, float y1, float x2, float y2, - float stepsize) + float frames_per_pixel) { - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ - int x1_offset = max_ff(v2d->cur.xmin, x1); - int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2); - if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { - int length = floor((x2_offset - x1_offset) / stepsize) + 1; - float ymid = (y1 + y2) / 2.0f; - float yscale = (y2 - y1) / 2.0f; - float samplestep; - float startsample, endsample; - float volume = seq->volume; - float value1, value2; - bSound *sound = seq->sound; - SoundWaveform *waveform; - - if (length < 2) { + /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid + * flickering when moving around the strip. + * To do this we figure out the fractional offset in pixel space by checking where the + * window starts. + * We then append this pixel offset to our strip start coordinate to ensure we are aligned to + * the screen pixel grid. */ + float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); + float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); + + /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ + float x1_offset = max_ff(v2d->cur.xmin, x1_adj); + float x2_offset = min_ff(v2d->cur.xmax, x2); + + /* Calculate how long the strip that is in view is in pixels. */ + int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel); + + if (pix_strip_len < 2) { return; } + bSound *sound = seq->sound; + BLI_spin_lock(sound->spinlock); if (!sound->waveform) { + /* Load the waveform data if it hasn't been loaded and cached already. */ if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { /* Prevent sounds from reloading. */ sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; @@ -277,87 +366,187 @@ static void draw_seq_waveform_overlay(View2D *v2d, } BLI_spin_unlock(sound->spinlock); - waveform = sound->waveform; + SoundWaveform *waveform = sound->waveform; /* Waveform could not be built. */ if (waveform->length == 0) { return; } - startsample = floor((seq->startofs + seq->anim_startofs) / FPS * - SOUND_WAVE_SAMPLES_PER_SECOND); - endsample = ceil((seq->startofs + seq->anim_startofs + seq->enddisp - seq->startdisp) / FPS * - SOUND_WAVE_SAMPLES_PER_SECOND); - samplestep = (endsample - startsample) * stepsize / (x2 - x1); + /* F-curve lookup is quite expensive, so do this after precondition. */ + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); - length = min_ii( - floor((waveform->length - startsample) / samplestep - (x1_offset - x1) / stepsize), - length); + WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2, + "tri_strip"); + WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len, + "line_strip"); - if (length < 2) { - return; - } + WaveVizData *tri_strip_iter = tri_strip_arr; + WaveVizData *line_strip_iter = line_strip_arr; - /* F-curve lookup is quite expensive, so do this after precondition. */ - FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + /* The y coordinate for the middle of the strip. */ + float y_mid = (y1 + y2) / 2.0f; + /* The length from the middle of the strip to the top/bottom. */ + float y_scale = (y2 - y1) / 2.0f; + float volume = seq->volume; - GPU_blend(GPU_BLEND_ALPHA); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(GPU_PRIM_TRI_STRIP, length * 2); + /* Value to keep track if the previous item to be drawn was a line strip. */ + int8_t was_line_strip = -1; /* -1 == no previous value. */ - for (int i = 0; i < length; i++) { - float sampleoffset = startsample + ((x1_offset - x1) / stepsize + i) * samplestep; - int p = sampleoffset; + float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; - value1 = waveform->data[p * 3]; - value2 = waveform->data[p * 3 + 1]; + /* How many samples do we have for each pixel? */ + float samples_per_pix = samples_per_frame * frames_per_pixel; - if (samplestep > 1.0f) { - for (int j = p + 1; (j < waveform->length) && (j < p + samplestep); j++) { - if (value1 > waveform->data[j * 3]) { - value1 = waveform->data[j * 3]; - } + float strip_start_offset = seq->startofs + seq->anim_startofs; + float start_sample = 0; - if (value2 < waveform->data[j * 3 + 1]) { - value2 = waveform->data[j * 3 + 1]; - } - } + if (strip_start_offset != 0) { + /* If start offset is not zero, we need to make sure that we pick the same start sample as if + * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering + * when changing start offset as the pixel alignment will not be the same for the drawn + * samples. */ + strip_start_offset = clamp_frame_coord_to_pixel( + x1 - strip_start_offset, pixel_frac, frames_per_pixel); + start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; + } + + start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* If we scrolled the start off-screen, then the start sample should be at the first visible + * sample. */ + start_sample += (x1_offset - x1_adj) * samples_per_frame; + + for (int i = 0; i < pix_strip_len; i++) { + float sample_offset = start_sample + i * samples_per_pix; + int p = sample_offset; + + if (p >= waveform->length) { + break; } - else if (p + 1 < waveform->length) { + + float value_min = waveform->data[p * 3]; + float value_max = waveform->data[p * 3 + 1]; + float rms = waveform->data[p * 3 + 2]; + + if (p + 1 < waveform->length) { /* Use simple linear interpolation. */ - float f = sampleoffset - p; - value1 = (1.0f - f) * value1 + f * waveform->data[p * 3 + 3]; - value2 = (1.0f - f) * value2 + f * waveform->data[p * 3 + 4]; + float f = sample_offset - p; + value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3]; + value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4]; + rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5]; + if (samples_per_pix > 1.0f) { + /* We need to sum up the values we skip over until the next step. */ + float next_pos = sample_offset + samples_per_pix; + int end_idx = next_pos; + + for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) { + value_min = min_ff(value_min, waveform->data[j * 3]); + value_max = max_ff(value_max, waveform->data[j * 3 + 1]); + rms = max_ff(rms, waveform->data[j * 3 + 2]); + } + } } if (fcu && !BKE_fcurve_is_empty(fcu)) { - float evaltime = x1_offset + (i * stepsize); + float evaltime = x1_offset + (i * frames_per_pixel); volume = evaluate_fcurve(fcu, evaltime); CLAMP_MIN(volume, 0.0f); } - value1 *= volume; - value2 *= volume; - if (value2 > 1 || value1 < -1) { - immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + value_min *= volume; + value_max *= volume; + rms *= volume; + + bool clipping = false; + + if (value_max > 1 || value_min < -1) { + clipping = true; - CLAMP_MAX(value2, 1.0f); - CLAMP_MIN(value1, -1.0f); + CLAMP_MAX(value_max, 1.0f); + CLAMP_MIN(value_min, -1.0f); } - else { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + + bool is_line_strip = (value_max - value_min < 0.05f); + + if (was_line_strip != -1 && is_line_strip != was_line_strip) { + /* If the previously added strip type isn't the same as the current one, + * add transition areas so they transition smoothly between each other. */ + if (is_line_strip) { + /* This will be a line strip, end the tri strip. */ + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_min * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = tri_strip_iter->pos[1]; + tri_strip_iter->end = true; + + /* End of section. */ + tri_strip_iter++; + + /* Check if we are at the end. + * If so, skip one point line. */ + if (i + 1 == pix_strip_len) { + continue; + } + } + else { + /* This will be a tri strip. */ + line_strip_iter--; + tri_strip_iter->pos[0] = line_strip_iter->pos[0]; + tri_strip_iter->pos[1] = line_strip_iter->pos[1]; + tri_strip_iter->clip = line_strip_iter->clip; + tri_strip_iter->rms_pos = line_strip_iter->pos[1]; + tri_strip_iter++; + + /* Check if line had only one point. */ + line_strip_iter--; + if (line_strip_iter < line_strip_arr || line_strip_iter->end) { + /* Only one point, skip it. */ + line_strip_iter++; + } + else { + /* End of section. */ + line_strip_iter++; + line_strip_iter->end = true; + line_strip_iter++; + } + } } - immVertex2f(pos, x1_offset + i * stepsize, ymid + value1 * yscale); - immVertex2f(pos, x1_offset + i * stepsize, ymid + value2 * yscale); + was_line_strip = is_line_strip; + + if (is_line_strip) { + line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + line_strip_iter->pos[1] = y_mid + value_min * y_scale; + line_strip_iter->clip = clipping; + line_strip_iter++; + } + else { + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_min * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale; + tri_strip_iter++; + + tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; + tri_strip_iter->pos[1] = y_mid + value_max * y_scale; + tri_strip_iter->clip = clipping; + tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale; + tri_strip_iter++; + } } - immEnd(); - immUnbindProgram(); - GPU_blend(GPU_BLEND_NONE); + WaveVizData *tri_strip_end = tri_strip_iter; + WaveVizData *line_strip_end = line_strip_iter; + + tri_strip_iter = tri_strip_arr; + line_strip_iter = line_strip_arr; + + draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false); + draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false); + draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true); + + MEM_freeN(tri_strip_arr); + MEM_freeN(line_strip_arr); } } @@ -1125,7 +1314,7 @@ static void draw_seq_strip(const bContext *C, } else { text_margin_y = y2; - y_threshold = 1; + y_threshold = false; } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1139,7 +1328,7 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { draw_sequence_extensions_overlay(scene, seq, pos, pixely); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 4b26469aad3..afad8999e88 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2943,7 +2943,7 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b) int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); - /** If strips have the same start frame favor the one with a higher channel. **/ + /* If strips have the same start frame favor the one with a higher channel. */ if (seq_a_start == seq_b_start) { return seq_a->machine > seq_b->machine; } diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c index 2dcc2d389d9..16d14b5fa72 100644 --- a/source/blender/editors/space_sequencer/sequencer_proxy.c +++ b/source/blender/editors/space_sequencer/sequencer_proxy.c @@ -131,7 +131,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { ListBase queue = {NULL, NULL}; LinkData *link; short stop = 0, do_update; @@ -197,7 +197,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) } LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { SEQ_proxy_set(seq, turnon); if (seq->strip->proxy == NULL) { diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 5980bfe37cd..333edd0ed5f 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -999,7 +999,9 @@ void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties. */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -1227,7 +1229,9 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* Properties. */ - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); ot->prop = RNA_def_enum(ot->srna, "side", sequencer_select_left_right_types, 0, "Side", ""); } diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 337ac2e0009..a0a9cdd96b1 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -275,7 +275,6 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) View2D *v2d = UI_view2d_fromcontext(C); ARegion *region = CTX_wm_region(C); Editing *ed = SEQ_editing_get(scene, false); - Sequence *last_seq = SEQ_select_active_get(scene); Sequence *seq; rctf cur_new = v2d->cur; @@ -293,7 +292,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((seq->flag & SELECT) || (seq == last_seq)) { + if (seq->flag & SELECT) { xmin = min_ii(xmin, seq->startdisp); xmax = max_ii(xmax, seq->enddisp); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 6de95f0995a..2a6e49edfb6 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -194,7 +194,7 @@ static void sequencer_free(SpaceLink *sl) SpaceSeq *sseq = (SpaceSeq *)sl; SequencerScopes *scopes = &sseq->scopes; - /* XXX if (sseq->gpd) BKE_gpencil_free(sseq->gpd); */ + /* XXX if (sseq->gpd) BKE_gpencil_free_data(sseq->gpd); */ if (scopes->zebra_ibuf) { IMB_freeImBuf(scopes->zebra_ibuf); @@ -364,10 +364,7 @@ static void sequencer_listener(const wmSpaceTypeListenerParams *params) /* ************* dropboxes ************* */ -static bool image_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -384,10 +381,7 @@ static bool image_drop_poll(bContext *C, return 0; } -static bool movie_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool movie_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -403,10 +397,7 @@ static bool movie_drop_poll(bContext *C, return 0; } -static bool sound_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool sound_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); @@ -448,9 +439,12 @@ static void sequencer_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL); - WM_dropbox_add(lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL); - WM_dropbox_add(lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL, NULL); + WM_dropbox_add( + lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index af783051661..89e92231657 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -312,10 +312,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region) /* ************* dropboxes ************* */ -static bool text_drop_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) { /* rule might not work? */ @@ -332,10 +329,7 @@ static void text_drop_copy(wmDrag *drag, wmDropBox *drop) RNA_string_set(drop->ptr, "filepath", drag->path); } -static bool text_drop_paste_poll(bContext *UNUSED(C), - wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) +static bool text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_ID); } @@ -356,8 +350,8 @@ static void text_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Text", SPACE_TEXT, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL); - WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL); + WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL, NULL); + WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL, NULL); } /* ************* end drop *********** */ diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 1d8bc427212..b6ba95885e4 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -686,10 +686,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *region) } } else { - if (drawcache->line_height) { - MEM_freeN(drawcache->line_height); - drawcache->line_height = NULL; - } + MEM_SAFE_FREE(drawcache->line_height); if (full_update || drawcache->update_flag) { nlines = BLI_listbase_count(&txt->lines); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index b98dae0cd57..2b78ecb245d 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -236,10 +236,7 @@ void text_update_line_edited(TextLine *line) } /* we just free format here, and let it rebuild during draw */ - if (line->format) { - MEM_freeN(line->format); - line->format = NULL; - } + MEM_SAFE_FREE(line->format); } void text_update_edited(Text *text) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 54f10e259f9..72d0c11e192 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -53,6 +53,7 @@ #include "BKE_screen.h" #include "BKE_workspace.h" +#include "ED_object.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -513,49 +514,49 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return WM_drag_is_ID_type(drag, id_type); } -static bool view3d_ob_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +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_collection_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_GR); } -static bool view3d_mat_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA); } -static bool view3d_object_data_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static char *view3d_mat_drop_tooltip(bContext *C, + wmDrag *drag, + const wmEvent *event, + struct wmDropBox *drop) +{ + const char *name = WM_drag_get_item_name(drag); + RNA_string_set(drop->ptr, "name", name); + return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); +} + +static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); - if (id_type) { - if (OB_DATA_SUPPORT_ID(id_type)) { - *r_tooltip = TIP_("Create object instance from object-data"); - return true; - } + if (id_type && OB_DATA_SUPPORT_ID(id_type)) { + return true; } return false; } -static bool view3d_ima_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **UNUSED(r_tooltip)) +static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), + wmDrag *UNUSED(drag), + const wmEvent *UNUSED(event), + wmDropBox *UNUSED(drop)) +{ + return BLI_strdup(TIP_("Create object instance from object-data")); +} + +static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), &event->x)) { return false; @@ -580,12 +581,9 @@ static bool view3d_ima_bg_is_camera_view(bContext *C) return false; } -static bool view3d_ima_bg_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) { + if (!view3d_ima_drop_poll(C, drag, event)) { return false; } @@ -596,12 +594,9 @@ static bool view3d_ima_bg_drop_poll(bContext *C, return view3d_ima_bg_is_camera_view(C); } -static bool view3d_ima_empty_drop_poll(bContext *C, - wmDrag *drag, - const wmEvent *event, - const char **r_tooltip) +static bool view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) { + if (!view3d_ima_drop_poll(C, drag, event)) { return false; } @@ -620,8 +615,7 @@ static bool view3d_ima_empty_drop_poll(bContext *C, static bool view3d_volume_drop_poll(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), - const char **UNUSED(r_tooltip)) + const wmEvent *UNUSED(event)) { return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } @@ -700,37 +694,44 @@ static void view3d_dropboxes(void) "OBJECT_OT_add_named", view3d_ob_drop_poll, view3d_ob_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, view3d_id_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + view3d_mat_drop_tooltip); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_volume_import", view3d_volume_drop_poll, view3d_id_path_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_collection_instance_add", view3d_collection_drop_poll, view3d_collection_drop_copy, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + NULL); WM_dropbox_add(lb, "OBJECT_OT_data_instance_add", view3d_object_data_drop_poll, view3d_id_drop_copy_with_type, - WM_drag_free_imported_drag_ID); + WM_drag_free_imported_drag_ID, + view3d_object_data_drop_tooltip); } static void view3d_widgets(void) diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 3428a738dde..b79303551a1 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1456,7 +1456,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "location", 0, NULL, ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, @@ -1472,7 +1472,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_quaternion", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE); if (RNA_boolean_get(ptr, "lock_rotations_4d")) { uiItemR(colsub, @@ -1496,7 +1496,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_axis_angle", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE); if (RNA_boolean_get(ptr, "lock_rotations_4d")) { uiItemR(colsub, @@ -1520,7 +1520,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "rotation_euler", 0, IFACE_("Rotation"), ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, @@ -1536,7 +1536,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr) colsub = uiLayoutColumn(split, true); uiItemR(colsub, ptr, "scale", 0, NULL, ICON_NONE); colsub = uiLayoutColumn(split, true); - uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE); + uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS); uiItemL(colsub, "", ICON_NONE); uiItemR(colsub, ptr, diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index d87c14b9844..ec99affe43b 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1876,7 +1876,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, if (own_ofs) { /* bind */ - ofs = GPU_offscreen_create(sizex, sizey, true, false, err_out); + ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out); if (ofs == NULL) { DRW_opengl_context_disable(); return NULL; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 651ae8a3000..b055a0fe947 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4917,10 +4917,7 @@ static int view3d_clipping_invoke(bContext *C, wmOperator *op, const wmEvent *ev if (rv3d->rflag & RV3D_CLIPPING) { rv3d->rflag &= ~RV3D_CLIPPING; ED_region_tag_redraw(region); - if (rv3d->clipbb) { - MEM_freeN(rv3d->clipbb); - } - rv3d->clipbb = NULL; + MEM_SAFE_FREE(rv3d->clipbb); return OPERATOR_FINISHED; } return WM_gesture_box_invoke(C, op, event); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 49299d73337..edc34d0d883 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -299,7 +299,9 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons ED_view3d_win_to_3d_int(ruler_info->area->spacedata.first, ruler_info->region, r_co, xy, r_co); } -/* use for mousemove events */ +/** + * Use for mouse-move events. + */ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, RulerInfo *ruler_info, RulerItem *ruler_item, diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 09936b41a74..1ac241013ed 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -548,7 +548,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->teleport.duration = U.walk_navigation.teleport_time; walk->mouse_speed = U.walk_navigation.mouse_speed; - if ((U.walk_navigation.flag & USER_WALK_GRAVITY)) { + if (U.walk_navigation.flag & USER_WALK_GRAVITY) { walk_navigation_mode_set(walk, WALK_MODE_GRAVITY); } else { @@ -563,7 +563,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->gravity_state = WALK_GRAVITY_STATE_OFF; - if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) { + if (walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]); } else { @@ -1199,11 +1199,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_FORWARD)) { + if (walk->active_directions & WALK_BIT_FORWARD) { direction += 1; } - if ((walk->active_directions & WALK_BIT_BACKWARD)) { + if (walk->active_directions & WALK_BIT_BACKWARD) { direction -= 1; } @@ -1223,11 +1223,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_LEFT)) { + if (walk->active_directions & WALK_BIT_LEFT) { direction += 1; } - if ((walk->active_directions & WALK_BIT_RIGHT)) { + if (walk->active_directions & WALK_BIT_RIGHT) { direction -= 1; } @@ -1245,11 +1245,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) direction = 0; - if ((walk->active_directions & WALK_BIT_UP)) { + if (walk->active_directions & WALK_BIT_UP) { direction -= 1; } - if ((walk->active_directions & WALK_BIT_DOWN)) { + if (walk->active_directions & WALK_BIT_DOWN) { direction = 1; } diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index d926ea84e0f..88efc530484 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -843,7 +843,7 @@ bool ED_view3d_unproject_v3( const int viewport[4] = {0, 0, region->winx, region->winy}; const float region_co[3] = {regionx, regiony, regionz}; - return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world); + return GPU_matrix_unproject_3fv(region_co, rv3d->viewinv, rv3d->winmat, viewport, world); } /** \} */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index ecf43c734e2..e3f97dd1c63 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1589,9 +1589,12 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); ot->prop = prop; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static Base *object_mouse_select_menu(bContext *C, @@ -1764,9 +1767,12 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE); ot->prop = prop; - RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static bool bone_mouse_select_menu(bContext *C, const uint *buffer, @@ -2150,7 +2156,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, for (a = 0; a < hits; a++) { if (has_bones) { /* skip non-bone objects */ - if ((buffer[4 * a + 3] & 0xFFFF0000)) { + if (buffer[4 * a + 3] & 0xFFFF0000) { if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) { basact = base; } @@ -2515,6 +2521,7 @@ static bool ed_object_select_pick(bContext *C, } /* also prevent making it active on mouse selection */ else if (BASE_SELECTABLE(v3d, basact)) { + const bool use_activate_selected_base = (oldbasact != basact) && (is_obedit == false); if (extend) { ED_object_base_select(basact, BA_SELECT); } @@ -2523,7 +2530,8 @@ static bool ed_object_select_pick(bContext *C, } else if (toggle) { if (basact->flag & BASE_SELECTED) { - if (basact == oldbasact) { + /* Keep selected if the base is to be activated. */ + if (use_activate_selected_base == false) { ED_object_base_select(basact, BA_DESELECT); } } @@ -2539,7 +2547,7 @@ static bool ed_object_select_pick(bContext *C, } } - if ((oldbasact != basact) && (is_obedit == false)) { + if (use_activate_selected_base) { ED_object_base_activate(C, basact); /* adds notifier */ if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) { WM_toolsystem_update_from_context_view3d(C); diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index ad0a330f0f4..e9efed3cd61 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -101,6 +101,7 @@ set(SRC transform_ops.c transform_orientations.c transform_snap.c + transform_snap_animation.c transform_snap_object.c transform_snap_sequencer.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index efcf7d587e1..4069a72a8fc 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -78,6 +78,9 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { + if (t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) { + return false; + } return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))); } @@ -1828,7 +1831,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) { + if (((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH) { BMEditMesh *em = NULL; /* BKE_editmesh_from_object(t->obedit); */ bool do_skip = false; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 1a61a594f37..549ad770ac6 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -582,7 +582,7 @@ typedef struct TransInfo { short around; /** space-type where transforming is. */ char spacetype; - /** Avoid looking inside #TransDataContainer.obedit. */ + /** Type of active object being edited. */ short obedit_type; /** translation, to show for widget. */ @@ -779,7 +779,6 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis void applyTransObjects(TransInfo *t); void restoreTransObjects(TransInfo *t); -void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); void calculateCenterLocal(TransInfo *t, const float center_global[3]); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 00fd008151d..094ae080de0 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -861,10 +861,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) /* The Action constraint only does this in the Before mode. */ bActionConstraint *data = (bActionConstraint *)con->data; - if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) && + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE, ACTCON_MIX_BEFORE_FULL) && ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) { return true; } + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) { + return true; + } } else if (con->type == CONSTRAINT_TYPE_TRANSFORM) { /* Transform constraint needs it for rotation at least (r.57309), @@ -1069,7 +1072,8 @@ static void init_proportional_edit(TransInfo *t) else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) { /* Already calculated by uv_set_connectivity_distance. */ } - else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) { + else if (convert_type == TC_CURVE_VERTS) { + BLI_assert(t->obedit_type == OB_CURVE); set_prop_dist(t, false); } else { @@ -1368,7 +1372,6 @@ void createTransData(bContext *C, TransInfo *t) switch (t->data_type) { case TC_ACTION_DATA: - t->obedit_type = -1; createTransActionData(C, t); break; case TC_POSE: @@ -1391,7 +1394,6 @@ void createTransData(bContext *C, TransInfo *t) createTransCurveVerts(t); break; case TC_GRAPH_EDIT_DATA: - t->obedit_type = -1; createTransGraphEditData(C, t); break; case TC_GPENCIL: @@ -1401,9 +1403,6 @@ void createTransData(bContext *C, TransInfo *t) createTransLatticeVerts(t); break; case TC_MASKING_DATA: - if (t->spacetype == SPACE_CLIP) { - t->obedit_type = -1; - } createTransMaskingData(C, t); break; case TC_MBALL_VERTS: @@ -1422,11 +1421,9 @@ void createTransData(bContext *C, TransInfo *t) createTransUVs(C, t); break; case TC_NLA_DATA: - t->obedit_type = -1; createTransNlaData(C, t); break; case TC_NODE_DATA: - t->obedit_type = -1; createTransNodeData(t); break; case TC_OBJECT: @@ -1470,12 +1467,10 @@ void createTransData(bContext *C, TransInfo *t) createTransSculpt(C, t); break; case TC_SEQ_DATA: - t->obedit_type = -1; t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ createTransSeqData(t); break; case TC_TRACKING_DATA: - t->obedit_type = -1; createTransTrackingData(C, t); break; case TC_NONE: @@ -1489,8 +1484,6 @@ void createTransData(bContext *C, TransInfo *t) countAndCleanTransDataContainer(t); init_proportional_edit(t); - - BLI_assert((!(t->flag & T_EDIT)) == (!(t->obedit_type != -1))); } /** \} */ @@ -1669,6 +1662,23 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } +void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float inv_unit_scale) +{ + /* If the handles are to be moved too + * (as side-effect of keyframes moving, to keep the general effect) + * offset them by the same amount so that the general angles are maintained + * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). + */ + if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { + td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; + td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + } + if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { + td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; + td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; + } +} + /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 971c23b8c69..fa34e2555d6 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -43,6 +43,10 @@ void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); +void transform_convert_flush_handle2D(TransData *td, + TransData2D *td2d, + const float inv_unit_scale); +void recalcData(TransInfo *t); /* transform_convert_mesh.c */ void transform_convert_mesh_customdatacorrect_init(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index cfa14e21d0d..a5565b5fb88 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -45,6 +45,8 @@ #include "WM_types.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" /* helper struct for gp-frame transforms */ @@ -140,19 +142,37 @@ static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, boo } /* This function assigns the information to transdata */ -static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos) +static void TimeToTransData( + TransData *td, TransData2D *td2d, BezTriple *bezt, AnimData *adt, float ypos) { - /* memory is calloc'ed, so that should zero everything nicely for us */ + float *time = bezt->vec[1]; + + /* Setup #TransData2D. */ + td2d->loc[0] = *time; + td2d->loc2d = time; + td2d->h1 = bezt->vec[0]; + td2d->h2 = bezt->vec[2]; + copy_v2_v2(td2d->ih1, td2d->h1); + copy_v2_v2(td2d->ih2, td2d->h2); + + /* Setup #TransData. */ + td->loc = time; /* Usually #td2d->loc is used here. But this is for when the original location is + not float[3]. */ td->val = time; - td->ival = *(time); - + td->ival = td->iloc[0] = *(time); td->center[0] = td->ival; td->center[1] = ypos; - /* store the AnimData where this keyframe exists as a keyframe of the - * active action as td->extra. - */ + /* Store the AnimData where this keyframe exists as a keyframe of the + * active action as #td->extra. */ td->extra = adt; + + if (bezt->f2 & SELECT) { + td->flag |= TD_SELECTED; + } + + /* Set flags to move handles as necessary. */ + td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; } /* This function advances the address to which td points to, so it must return @@ -185,19 +205,7 @@ static TransData *ActionFCurveToTransData(TransData *td, * so can't use BEZT_ISSEL_ANY() macro */ /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - TimeToTransData(td, bezt->vec[1], adt, ypos); - - if (bezt->f2 & SELECT) { - td->flag |= TD_SELECTED; - } - - /* Set flags to move handles as necessary. */ - td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; - td2d->h1 = bezt->vec[0]; - td2d->h2 = bezt->vec[2]; - - copy_v2_v2(td2d->ih1, td2d->h1); - copy_v2_v2(td2d->ih2, td2d->h2); + TimeToTransData(td, td2d, bezt, adt, ypos); td++; td2d++; @@ -233,16 +241,15 @@ static int GPLayerToTransData(TransData *td, for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { - /* memory is calloc'ed, so that should zero everything nicely for us */ - td->val = &tfd->val; - td->ival = (float)gpf->framenum; + tfd->val = (float)gpf->framenum; + tfd->sdata = &gpf->framenum; + + td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */ + td->ival = td->iloc[0] = (float)gpf->framenum; td->center[0] = td->ival; td->center[1] = ypos; - tfd->val = (float)gpf->framenum; - tfd->sdata = &gpf->framenum; - /* Advance `td` now. */ td++; tfd++; @@ -598,6 +605,19 @@ void recalcData_actedit(TransInfo *t) flushTransIntFrameActionData(t); } + /* Flush 2d vector. */ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + const short autosnap = getAnimEdit_SnapMode(t); + TransData *td; + TransData2D *td2d; + int i = 0; + for (td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); + } + transform_convert_flush_handle2D(td, td2d, 1.0f); + } + if (ac.datatype != ANIMCONT_MASK) { /* Get animdata blocks visible in editor, * assuming that these will be the ones where things changed. */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 1f1b1f8db97..5627a910ab4 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -131,12 +131,12 @@ static void autokeyframe_pose( ListBase dsources = {NULL, NULL}; - /* add datasource override for the camera object */ + /* Add data-source override for the camera object. */ ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); /* only insert into active keyingset? */ if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { - /* run the active Keying Set on the current datasource */ + /* Run the active Keying Set on the current data-source. */ ANIM_apply_keyingset( C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time); } @@ -1515,7 +1515,7 @@ int transform_convert_pose_transflags_update(Object *ob, for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; if (PBONE_VISIBLE(arm, bone)) { - if ((bone->flag & BONE_SELECTED)) { + if (bone->flag & BONE_SELECTED) { bone->flag |= BONE_TRANSFORM; } else { diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 111f81ff87b..d22277f9d91 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -40,7 +40,12 @@ #include "UI_view2d.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" +#include "transform_snap.h" + +#include "transform_mode.h" typedef struct TransDataGraph { float unit_scale; @@ -656,14 +661,12 @@ static bool fcu_test_selected(FCurve *fcu) */ static void flushTransGraphData(TransInfo *t) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; TransData *td; TransData2D *td2d; TransDataGraph *tdg; - Scene *scene = t->scene; - double secf = FPS; int a; + const short autosnap = getAnimEdit_SnapMode(t); TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* flush to 2d vector from internally used 3d vector */ @@ -679,21 +682,8 @@ static void flushTransGraphData(TransInfo *t) * - Only apply to keyframes (but never to handles). * - Don't do this when canceling, or else these changes won't go away. */ - if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) { - switch (sipo->autosnap) { - case SACTSNAP_FRAME: /* snap to nearest frame */ - td2d->loc[0] = floor((double)td2d->loc[0] + 0.5); - break; - - case SACTSNAP_SECOND: /* snap to nearest second */ - td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf; - break; - - case SACTSNAP_MARKER: /* snap to nearest marker */ - td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, - td2d->loc[0]); - break; - } + if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); } /* we need to unapply the nla-mapping from the time in some situations */ @@ -704,32 +694,6 @@ static void flushTransGraphData(TransInfo *t) td2d->loc2d[0] = td2d->loc[0]; } - /** Time-stepping auto-snapping modes don't get applied for Graph Editor transforms, - * as these use the generic transform modes which don't account for this sort of thing. - * These ones aren't affected by NLA mapping, so we do this after the conversion... - * - * \note We also have to apply to td->loc, - * as that's what the handle-adjustment step below looks to, - * otherwise we get "swimming handles". - * - * \note We don't do this when canceling transforms, or else these changes don't go away. - */ - if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 && - ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) { - switch (sipo->autosnap) { - case SACTSNAP_STEP: /* frame step */ - td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5); - td->loc[0] = floor((double)td->loc[0] + 0.5); - break; - - case SACTSNAP_TSTEP: /* second step */ - /* XXX: the handle behavior in this case is still not quite right... */ - td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf; - td->loc[0] = floor(((double)td->loc[0] / secf) + 0.5) * secf; - break; - } - } - /* if int-values only, truncate to integers */ if (td->flag & TD_INTVALUES) { td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f); @@ -738,15 +702,7 @@ static void flushTransGraphData(TransInfo *t) td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset; } - if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { - td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; - td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; - } - - if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { - td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; - td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; - } + transform_convert_flush_handle2D(td, td2d, inv_unit_scale); } } diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c index 54df8270702..1a25cfd1efb 100644 --- a/source/blender/editors/transform/transform_convert_mask.c +++ b/source/blender/editors/transform/transform_convert_mask.c @@ -293,7 +293,7 @@ void createTransMaskingData(bContext *C, TransInfo *t) for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } @@ -351,7 +351,7 @@ void createTransMaskingData(bContext *C, TransInfo *t) for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) { continue; } diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index b55005673d9..7e5b80c2453 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -41,7 +41,12 @@ #include "RNA_access.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" +#include "transform_snap.h" + +#include "transform_mode.h" /** Used for NLA transform (stored in #TransData.extra pointer). */ typedef struct TransDataNla { @@ -289,21 +294,30 @@ void createTransNlaData(bContext *C, TransInfo *t) void recalcData_nla(TransInfo *t) { SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; - Scene *scene = t->scene; - double secf = FPS; - int i; TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - TransDataNla *tdn = tc->custom.type.data; + + /* handle auto-snapping + * NOTE: only do this when transform is still running, or we can't restore + */ + if (t->state != TRANS_CANCEL) { + const short autosnap = getAnimEdit_SnapMode(t); + if (autosnap != SACTSNAP_OFF) { + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + transform_snap_anim_flush_data(t, td, autosnap, td->loc); + } + } + } /* For each strip we've got, perform some additional validation of the values * that got set before using RNA to set the value (which does some special * operations when setting these values to make sure that everything works ok). */ - for (i = 0; i < tc->data_len; i++, tdn++) { + TransDataNla *tdn = tc->custom.type.data; + for (int i = 0; i < tc->data_len; i++, tdn++) { NlaStrip *strip = tdn->strip; PointerRNA strip_ptr; - short iter; int delta_y1, delta_y2; /* if this tdn has no handles, that means it is just a dummy that should be skipped */ @@ -367,8 +381,7 @@ void recalcData_nla(TransInfo *t) next = next->next; } - for (iter = 0; iter < 5; iter++) { - + for (short iter = 0; iter < 5; iter++) { const bool pExceeded = (prev != NULL) && (tdn->h1[0] < prev->end); const bool nExceeded = (next != NULL) && (tdn->h2[0] > next->start); @@ -407,50 +420,6 @@ void recalcData_nla(TransInfo *t) } } - /* handle auto-snapping - * NOTE: only do this when transform is still running, or we can't restore - */ - if (t->state != TRANS_CANCEL) { - switch (snla->autosnap) { - case SACTSNAP_FRAME: /* snap to nearest frame */ - case SACTSNAP_STEP: /* frame step - this is basically the same, - * since we don't have any remapping going on */ - { - tdn->h1[0] = floorf(tdn->h1[0] + 0.5f); - tdn->h2[0] = floorf(tdn->h2[0] + 0.5f); - break; - } - - case SACTSNAP_SECOND: /* snap to nearest second */ - case SACTSNAP_TSTEP: /* second step - this is basically the same, - * since we don't have any remapping going on */ - { - /* This case behaves differently from the rest, since lengths of strips - * may not be multiples of a second. If we just naively resize adjust - * the handles, things may not work correctly. Instead, we only snap - * the first handle, and move the other to fit. - * - * FIXME: we do run into problems here when user attempts to negatively - * scale the strip, as it then just compresses down and refuses - * to expand out the other end. - */ - float h1_new = (float)(floor(((double)tdn->h1[0] / secf) + 0.5) * secf); - float delta = h1_new - tdn->h1[0]; - - tdn->h1[0] = h1_new; - tdn->h2[0] += delta; - break; - } - - case SACTSNAP_MARKER: /* snap to nearest marker */ - { - tdn->h1[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h1[0]); - tdn->h2[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h2[0]); - break; - } - } - } - /* Use RNA to write the values to ensure that constraints on these are obeyed * (e.g. for transition strips, the values are taken from the neighbors) * diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index ee6cb391fdc..bcbac009948 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -749,7 +749,7 @@ static void autokeyframe_object( /* Get flags used for inserting keyframes. */ flag = ANIM_get_keyframing_flags(scene, true); - /* add datasource override for the object */ + /* Add data-source override for the object. */ ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) { diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index e89ab6729d2..81fc1496b1a 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -60,6 +60,7 @@ #include "UI_view2d.h" #include "transform.h" +#include "transform_convert.h" #include "transform_mode.h" #include "transform_orientations.h" #include "transform_snap.h" @@ -152,8 +153,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->flag = 0; - if (obact && !(t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) && - ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) { + if (obact && ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) { t->obedit_type = obact->type; } else { diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 65a673940f8..8df95222fa1 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -968,9 +968,9 @@ void ElementResize(const TransInfo *t, float obsizemat[3][3]; /* Reorient the size mat to fit the oriented object. */ mul_m3_m3m3(obsizemat, tmat, td->axismtx); - /* print_m3("obsizemat", obsizemat); */ + // print_m3("obsizemat", obsizemat); TransMat3ToSize(obsizemat, td->axismtx, fsize); - /* print_v3("fsize", fsize); */ + // print_v3("fsize", fsize); } else { mat3_to_size(fsize, tmat); @@ -1068,102 +1068,6 @@ void ElementResize(const TransInfo *t, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Transform (Frame Utils) - * \{ */ - -/** - * This function returns the snapping 'mode' for Animation Editors only. - * We cannot use the standard snapping due to NLA-strip scaling complexities. - * - * TODO: these modifier checks should be key-mappable. - */ -short getAnimEdit_SnapMode(TransInfo *t) -{ - short autosnap = SACTSNAP_OFF; - - if (t->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; - - if (saction) { - autosnap = saction->autosnap; - } - } - else if (t->spacetype == SPACE_GRAPH) { - SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - - if (sipo) { - autosnap = sipo->autosnap; - } - } - else if (t->spacetype == SPACE_NLA) { - SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; - - if (snla) { - autosnap = snla->autosnap; - } - } - else { - autosnap = SACTSNAP_OFF; - } - - /* toggle autosnap on/off - * - when toggling on, prefer nearest frame over 1.0 frame increments - */ - if (t->modifiers & MOD_SNAP_INVERT) { - if (autosnap) { - autosnap = SACTSNAP_OFF; - } - else { - autosnap = SACTSNAP_FRAME; - } - } - - return autosnap; -} - -/* This function is used by Animation Editor specific transform functions to do - * the Snap Keyframe to Nearest Frame/Marker - */ -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap) -{ - if (autosnap != SACTSNAP_OFF) { - float val; - - /* convert frame to nla-action time (if needed) */ - if (adt && (t->spacetype != SPACE_SEQ)) { - val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); - } - else { - val = *(td->val); - } - - snapFrameTransform(t, autosnap, true, val, &val); - - /* convert frame out of nla-action time */ - if (adt && (t->spacetype != SPACE_SEQ)) { - *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); - } - else { - *(td->val) = val; - } - } - - /* If the handles are to be moved too - * (as side-effect of keyframes moving, to keep the general effect) - * offset them by the same amount so that the general angles are maintained - * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). - */ - if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { - td2d->h1[0] = td2d->ih1[0] + *td->val - td->ival; - } - if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { - td2d->h2[0] = td2d->ih2[0] + *td->val - td->ival; - } -} -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Transform Mode Initialization * \{ */ diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 027fb6b6982..d8601000ddb 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -63,9 +63,6 @@ void ElementResize(const TransInfo *t, const TransDataContainer *tc, TransData *td, const float mat[3][3]); -short getAnimEdit_SnapMode(TransInfo *t); -void doAnimEdit_SnapFrame( - TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode); void transform_mode_default_modal_orientation_set(TransInfo *t, int type); diff --git a/source/blender/editors/transform/transform_mode_align.c b/source/blender/editors/transform/transform_mode_align.c index 5bc2aa68443..1a1d84699f4 100644 --- a/source/blender/editors/transform/transform_mode_align.c +++ b/source/blender/editors/transform/transform_mode_align.c @@ -32,6 +32,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_baketime.c b/source/blender/editors/transform/transform_mode_baketime.c index 5efed6920dc..653944b56a7 100644 --- a/source/blender/editors/transform/transform_mode_baketime.c +++ b/source/blender/editors/transform/transform_mode_baketime.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bake-Time) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c index e827e604327..95e2d944b9b 100644 --- a/source/blender/editors/transform/transform_mode_bbone_resize.c +++ b/source/blender/editors/transform/transform_mode_bbone_resize.c @@ -37,9 +37,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (EditBone B-Bone width scaling) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index 850d26571cd..6d84c397fa6 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -44,9 +44,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bend) Custom Data * \{ */ diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c index ced159a76c9..da7393ab42e 100644 --- a/source/blender/editors/transform/transform_mode_boneenvelope.c +++ b/source/blender/editors/transform/transform_mode_boneenvelope.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bone Envelope) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_boneroll.c b/source/blender/editors/transform/transform_mode_boneroll.c index da6c0b44c3a..cd04ca2b844 100644 --- a/source/blender/editors/transform/transform_mode_boneroll.c +++ b/source/blender/editors/transform/transform_mode_boneroll.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (EditBone Roll) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index 68416c780ef..9433502ef55 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Curve Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 425bfec241e..5466ba3e91f 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Bevel Weight) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index 91e2507e544..1d3b4dbb4f0 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Crease) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index 6f2bcc148ce..1f57bacf78f 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -35,9 +35,10 @@ #include "UI_interface.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" /* -------------------------------------------------------------------- */ /** \name Transform (Normal Rotation) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 066a2853dc7..cfcb17b8da0 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -852,7 +852,7 @@ static EdgeSlideData *createEdgeSlideVerts_double_side(TransInfo *t, TransDataCo #undef EDGESLIDE_VERT_IS_INNER } - /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + // EDBM_flag_disable_all(em, BM_ELEM_SELECT); BLI_assert(STACK_SIZE(sv_array) == (uint)sv_tot); @@ -1037,7 +1037,7 @@ static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataCo } } - /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + // EDBM_flag_disable_all(em, BM_ELEM_SELECT); sld->sv = sv_array; sld->totsv = sv_tot; diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 7c496d271ef..748769491f1 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -38,9 +38,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (GPencil Strokes Opacity) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 608a49f38b1..bc081edd597 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -38,9 +38,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (GPencil Strokes Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index cfbd6030788..327a639773c 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Mask Shrink/Fatten) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c index f225f1a7c06..2ae32f3545a 100644 --- a/source/blender/editors/transform/transform_mode_mirror.c +++ b/source/blender/editors/transform/transform_mode_mirror.c @@ -37,6 +37,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c index 0492ec8df8c..0527d1bc08e 100644 --- a/source/blender/editors/transform/transform_mode_push_pull.c +++ b/source/blender/editors/transform/transform_mode_push_pull.c @@ -38,9 +38,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Push/Pull) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 44a29cfac45..bfbdaa389f4 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -34,9 +34,11 @@ #include "UI_interface.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Rotation) Matrix Cache * \{ */ diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index f5672887905..018725ec6dd 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -41,9 +41,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Shear) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index 4cdaab599b4..b96b8103392 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -40,9 +40,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Shrink-Fatten) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 0a7eea8a989..236c9024201 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -35,9 +35,11 @@ #include "transform.h" #include "transform_constraints.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Skin) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_tilt.c b/source/blender/editors/transform/transform_mode_tilt.c index d3b72fdf503..b48f474e16e 100644 --- a/source/blender/editors/transform/transform_mode_tilt.c +++ b/source/blender/editors/transform/transform_mode_tilt.c @@ -36,9 +36,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Tilt) * \{ */ diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 7ae97c66660..50fd714727b 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -39,6 +39,9 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" +#include "transform_snap.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ @@ -62,7 +65,6 @@ static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeScaleValue(TransInfo *t, float value) { Scene *scene = t->scene; - const short autosnap = getAnimEdit_SnapMode(t); FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *td = tc->data; @@ -86,9 +88,6 @@ static void applyTimeScaleValue(TransInfo *t, float value) /* now, calculate the new value */ *(td->val) = ((td->ival - startx) * fac) + startx; - - /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } } } diff --git a/source/blender/editors/transform/transform_mode_timeslide.c b/source/blender/editors/transform/transform_mode_timeslide.c index 34d3251f9cf..5cc53eb08ce 100644 --- a/source/blender/editors/transform/transform_mode_timeslide.c +++ b/source/blender/editors/transform/transform_mode_timeslide.c @@ -42,6 +42,8 @@ #include "BLT_translation.h" #include "transform.h" +#include "transform_convert.h" + #include "transform_mode.h" /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c index 948242e547f..294040946bd 100644 --- a/source/blender/editors/transform/transform_mode_timetranslate.c +++ b/source/blender/editors/transform/transform_mode_timetranslate.c @@ -39,9 +39,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Animation Translation) * \{ */ @@ -57,10 +59,18 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) } else { const short autosnap = getAnimEdit_SnapMode(t); - float val = t->values_final[0]; + float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; + float val = ival + t->values_final[0]; - float snap_val; - snapFrameTransform(t, autosnap, false, val, &snap_val); + float snap_val = val; + snapFrameTransform(t, autosnap, ival, val, &snap_val); + + if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) { + /* Convert to seconds. */ + const Scene *scene = t->scene; + const double secf = FPS; + snap_val /= secf; + } if (autosnap == SACTSNAP_FRAME) { BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", snap_val, val); @@ -86,24 +96,11 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeTranslateValue(TransInfo *t, const float deltax) { - const short autosnap = getAnimEdit_SnapMode(t); - FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* It doesn't matter whether we apply to t->data. */ TransData *td = tc->data; - TransData2D *td2d = tc->data_2d; - /* It doesn't matter whether we apply to t->data or - * t->data2d, but t->data2d is more convenient. */ - for (int i = 0; i < tc->data_len; i++, td++, td2d++) { - /* It is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from. - * (this is only valid when not in NLA) - * (also: masks and gpencil don't have animadata) - */ - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - - /* apply nearest snapping */ - *(td->val) = td->ival + deltax * td->factor; - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); + for (int i = 0; i < tc->data_len; i++, td++) { + *(td->val) = td->loc[0] = td->ival + deltax * td->factor; } } } diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c index 8587d5ae140..bfc85b2fe44 100644 --- a/source/blender/editors/transform/transform_mode_tosphere.c +++ b/source/blender/editors/transform/transform_mode_tosphere.c @@ -39,9 +39,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name To Sphere Utilities * \{ */ diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c index 68177c6becf..aa8b0783d0a 100644 --- a/source/blender/editors/transform/transform_mode_trackball.c +++ b/source/blender/editors/transform/transform_mode_trackball.c @@ -37,9 +37,11 @@ #include "BLT_translation.h" #include "transform.h" -#include "transform_mode.h" +#include "transform_convert.h" #include "transform_snap.h" +#include "transform_mode.h" + /* -------------------------------------------------------------------- */ /** \name Transform (Rotation - Trackball) Element * \{ */ diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 75744f26c15..e44e346d3e4 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -221,22 +221,30 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ } else { float dvec[3]; + copy_v3_v3(dvec, vec); + if (t->spacetype == SPACE_GRAPH) { + /* WORKAROUND: + * Special case where snapping is done in #recalData. + * Update the header based on the first element. */ + const short autosnap = getAnimEdit_SnapMode(t); + float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival; + float val = ival + dvec[0]; + snapFrameTransform(t, autosnap, ival, val, &dvec[0]); + } + if (t->con.mode & CON_APPLY) { int i = 0; zero_v3(dvec); if (t->con.mode & CON_AXIS0) { - dvec[i++] = vec[0]; + dvec[i++] = dvec[0]; } if (t->con.mode & CON_AXIS1) { - dvec[i++] = vec[1]; + dvec[i++] = dvec[1]; } if (t->con.mode & CON_AXIS2) { - dvec[i++] = vec[2]; + dvec[i++] = dvec[2]; } } - else { - copy_v3_v3(dvec, vec); - } if (t->flag & T_2D_EDIT) { applyAspectRatio(t, dvec); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 2c424d8ace3..45c077b8a07 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -371,25 +371,24 @@ static void transformops_exit(bContext *C, wmOperator *op) G.moving = 0; } +static int transformops_mode(wmOperator *op) +{ + for (TransformModeItem *tmode = transform_modes; tmode->idname; tmode++) { + if (op->type->idname == tmode->idname) { + return tmode->mode; + } + } + + return RNA_enum_get(op->ptr, "mode"); +} + static int transformops_data(bContext *C, wmOperator *op, const wmEvent *event) { int retval = 1; if (op->customdata == NULL) { TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data2"); - TransformModeItem *tmode; - int mode = -1; - - for (tmode = transform_modes; tmode->idname; tmode++) { - if (op->type->idname == tmode->idname) { - mode = tmode->mode; - break; - } - } - - if (mode == -1) { - mode = RNA_enum_get(op->ptr, "mode"); - } + int mode = transformops_mode(op); retval = initTransform(C, t, op, event, mode); /* store data */ @@ -556,6 +555,16 @@ static bool transform_poll_property(const bContext *UNUSED(C), } } + /* Orientation Axis. */ + { + if (STREQ(prop_id, "orient_axis")) { + eTfmMode mode = (eTfmMode)transformops_mode(op); + if (mode == TFM_ALIGN) { + return false; + } + } + } + /* Proportional Editing. */ { PropertyRNA *prop_pet = RNA_struct_find_property(op->ptr, "use_proportional_edit"); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2619fdf3403..05a20a14477 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -575,7 +575,7 @@ static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; /* All obedit types will match. */ - const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1; + const int obedit_type = t->obedit_type; ViewLayer *view_layer = t->view_layer; Base *base_act = view_layer->basact; @@ -594,7 +594,7 @@ static void initSnappingMode(TransInfo *t) else if (t->spacetype == SPACE_SEQ) { t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene); } - else { + else if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { /* force project off when not supported */ if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) { t->tsnap.project = 0; @@ -608,16 +608,31 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode |= SCE_SNAP_MODE_GRID; } } + else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) { + /* No incremental snapping. */ + t->tsnap.mode = 0; + } + else { + /* Fallback. */ + t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; + } - if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && - (t->options & CTX_CAMERA) == 0) { + if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { /* Only 3D view or UV. */ /* Not with camera selected in camera view. */ setSnappingCallback(t); - if ((obedit_type != -1) && - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { + /* In "Edit Strokes" mode, + * snap tool can perform snap to selected or active objects (see T49632) + * TODO: perform self snap in gpencil_strokes. + * + * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ + t->tsnap.modeSelect = SNAP_ALL; + } + else if ((obedit_type != -1) && + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { /* Edit mode */ /* Temporary limited to edit mode meshes, armature, curves, metaballs. */ @@ -636,17 +651,7 @@ static void initSnappingMode(TransInfo *t) } else if (obedit_type == -1) { /* Object mode */ - if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { - /* In "Edit Strokes" mode, - * snap tool can perform snap to selected or active objects (see T49632) - * TODO: perform self snap in gpencil_strokes. - * - * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ - t->tsnap.modeSelect = SNAP_ALL; - } - else { - t->tsnap.modeSelect = SNAP_NOT_SELECTED; - } + t->tsnap.modeSelect = SNAP_NOT_SELECTED; } else { /* Increment if snap is not possible */ @@ -657,10 +662,6 @@ static void initSnappingMode(TransInfo *t) setSnappingCallback(t); t->tsnap.modeSelect = SNAP_NOT_SELECTED; } - else { - /* Fallback. */ - t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; - } if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { @@ -1464,47 +1465,9 @@ bool snapNodesTransform( /** \} */ /* -------------------------------------------------------------------- */ -/** \name snap Frames +/** \name snap Grid * \{ */ -/* This function is used by Animation Editor specific transform functions to do - * the Snap Keyframe to Nearest Frame/Marker - */ -void snapFrameTransform(TransInfo *t, - const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - float *r_val) -{ - double val = delta; - switch (autosnap) { - case SACTSNAP_STEP: - case SACTSNAP_FRAME: - val = floor(val + 0.5); - break; - case SACTSNAP_MARKER: - /* snap to nearest marker */ - /* TODO: need some more careful checks for where data comes from. */ - val = ED_markers_find_nearest_marker_time(&t->scene->markers, (float)val); - break; - case SACTSNAP_SECOND: - case SACTSNAP_TSTEP: { - /* second step */ - const Scene *scene = t->scene; - const double secf = FPS; - val = floor((val / secf) + 0.5); - if (is_frame_value) { - val *= secf; - } - break; - } - case SACTSNAP_OFF: { - break; - } - } - *r_val = (float)val; -} - static void snap_grid_apply( TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3]) { diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h index 6dfaeab93e6..ed7f93304bc 100644 --- a/source/blender/editors/transform/transform_snap.h +++ b/source/blender/editors/transform/transform_snap.h @@ -45,12 +45,6 @@ bool snapNodesTransform(struct TransInfo *t, float r_loc[2], float *r_dist_px, char *r_node_border); -void snapFrameTransform(struct TransInfo *t, - const eAnimEdit_AutoSnap autosnap, - const bool is_frame_value, - const float delta, - /* return args */ - float *r_val); bool transformModeUseSnap(const TransInfo *t); @@ -86,3 +80,15 @@ struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) void transform_snap_sequencer_data_free(struct TransSeqSnapData *data); bool transform_snap_sequencer_calc(struct TransInfo *t); void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec); + +/* transform_snap_animation.c */ +short getAnimEdit_SnapMode(TransInfo *t); +void snapFrameTransform(TransInfo *t, + const eAnimEdit_AutoSnap autosnap, + const float val_initial, + const float val_final, + float *r_val_final); +void transform_snap_anim_flush_data(TransInfo *t, + TransData *td, + const eAnimEdit_AutoSnap autosnap, + float *r_val_final); diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c new file mode 100644 index 00000000000..08335924ddf --- /dev/null +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -0,0 +1,159 @@ +/* + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup edtransform + */ + +#include "DNA_anim_types.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_nla.h" + +#include "ED_markers.h" +#include "ED_screen.h" + +#include "transform.h" +#include "transform_snap.h" + +/* -------------------------------------------------------------------- */ +/** \name Snapping in Anim Editors + * \{ */ + +/** + * This function returns the snapping 'mode' for Animation Editors only. + * We cannot use the standard snapping due to NLA-strip scaling complexities. + * + * TODO: these modifier checks should be accessible from the key-map. + */ +short getAnimEdit_SnapMode(TransInfo *t) +{ + short autosnap = SACTSNAP_OFF; + + if (t->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)t->area->spacedata.first; + + if (saction) { + autosnap = saction->autosnap; + } + } + else if (t->spacetype == SPACE_GRAPH) { + SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; + + if (sipo) { + autosnap = sipo->autosnap; + } + } + else if (t->spacetype == SPACE_NLA) { + SpaceNla *snla = (SpaceNla *)t->area->spacedata.first; + + if (snla) { + autosnap = snla->autosnap; + } + } + else { + autosnap = SACTSNAP_OFF; + } + + /* toggle autosnap on/off + * - when toggling on, prefer nearest frame over 1.0 frame increments + */ + if (t->modifiers & MOD_SNAP_INVERT) { + if (autosnap) { + autosnap = SACTSNAP_OFF; + } + else { + autosnap = SACTSNAP_FRAME; + } + } + + return autosnap; +} + +void snapFrameTransform(TransInfo *t, + const eAnimEdit_AutoSnap autosnap, + const float val_initial, + const float val_final, + float *r_val_final) +{ + float deltax = val_final - val_initial; + switch (autosnap) { + case SACTSNAP_FRAME: + *r_val_final = floorf(val_final + 0.5f); + break; + case SACTSNAP_MARKER: + /* Snap to nearest marker. */ + /* TODO: need some more careful checks for where data comes from. */ + *r_val_final = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val_final); + break; + case SACTSNAP_SECOND: + case SACTSNAP_TSTEP: { + const Scene *scene = t->scene; + const double secf = FPS; + if (autosnap == SACTSNAP_SECOND) { + *r_val_final = floorf((val_final / secf) + 0.5) * secf; + } + else { + deltax = (float)(floor((deltax / secf) + 0.5) * secf); + *r_val_final = val_initial + deltax; + } + break; + } + case SACTSNAP_STEP: + deltax = floorf(deltax + 0.5f); + *r_val_final = val_initial + deltax; + break; + case SACTSNAP_OFF: + break; + } +} + +/* This function is used by Animation Editor specific transform functions to do + * the Snap Keyframe to Nearest Frame/Marker + */ +void transform_snap_anim_flush_data(TransInfo *t, + TransData *td, + const eAnimEdit_AutoSnap autosnap, + float *r_val_final) +{ + BLI_assert(autosnap != SACTSNAP_OFF); + + float val = td->loc[0]; + float ival = td->iloc[0]; + AnimData *adt = (!ELEM(t->spacetype, SPACE_NLA, SPACE_SEQ)) ? td->extra : NULL; + + /* Convert frame to nla-action time (if needed) */ + if (adt) { + val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP); + ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP); + } + + snapFrameTransform(t, autosnap, ival, val, &val); + + /* Convert frame out of nla-action time. */ + if (adt) { + val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); + } + + *r_val_final = val; +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index a1f396eb503..6e926f36fba 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -141,7 +141,7 @@ static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); SeqCollection *snap_targets = SEQ_collection_create(__func__); LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if ((seq->flag & SELECT)) { + if (seq->flag & SELECT) { continue; /* Selected are being transformed. */ } if ((seq->flag & SEQ_MUTE) && (snap_flag & SEQ_SNAP_IGNORE_MUTED)) { diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 54ec6b22e70..b396e348845 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC ../include/ED_info.h ../include/ED_keyframes_draw.h ../include/ED_keyframes_edit.h + ../include/ED_keyframes_keylist.h ../include/ED_keyframing.h ../include/ED_lattice.h ../include/ED_markers.h diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 15d672dea56..823837e2a42 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -309,7 +309,7 @@ bool user_string_to_number(bContext *C, return success; #else - UNUSED_VARS(C, unit, type); + UNUSED_VARS(C, unit, type, use_single_line_error, r_error); *r_value = atof(str); return true; #endif diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index f97403a0919..b4bdeace716 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -3399,10 +3399,7 @@ static void p_chart_lscm_end(PChart *chart) EIG_linear_solver_delete(chart->u.lscm.context); } - if (chart->u.lscm.abf_alpha) { - MEM_freeN(chart->u.lscm.abf_alpha); - chart->u.lscm.abf_alpha = NULL; - } + MEM_SAFE_FREE(chart->u.lscm.abf_alpha); chart->u.lscm.context = NULL; chart->u.lscm.pin1 = NULL; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 20aadb84b7b..5a82cd31112 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2140,11 +2140,12 @@ void UV_OT_select(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "deselect_all", false, @@ -2152,7 +2153,7 @@ void UV_OT_select(wmOperatorType *ot) "Deselect all when nothing under the cursor"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_float_vector( + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2163,6 +2164,7 @@ void UV_OT_select(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -2296,12 +2298,14 @@ void UV_OT_select_loop(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* requires space image */ /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_float_vector( + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2312,6 +2316,7 @@ void UV_OT_select_loop(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -2494,17 +2499,20 @@ void UV_OT_select_linked_pick(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* requires space image */ /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_boolean(ot->srna, - "deselect", - 0, - "Deselect", - "Deselect linked UV vertices rather than selecting them"); - RNA_def_float_vector( + PropertyRNA *prop; + prop = RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "deselect", + 0, + "Deselect", + "Deselect linked UV vertices rather than selecting them"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float_vector( ot->srna, "location", 2, @@ -2515,6 +2523,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot) "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -3903,7 +3912,7 @@ BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_v BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); - if ((luv->flag & MLOOPUV_VERTSEL)) { + if (luv->flag & MLOOPUV_VERTSEL) { BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); verts[verts_len++] = l_iter; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 535a0e00347..c5485cc1495 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -265,26 +265,11 @@ static StitchPreviewer *stitch_preview_init(void) static void stitch_preview_delete(StitchPreviewer *stitch_preview) { if (stitch_preview) { - if (stitch_preview->preview_polys) { - MEM_freeN(stitch_preview->preview_polys); - stitch_preview->preview_polys = NULL; - } - if (stitch_preview->uvs_per_polygon) { - MEM_freeN(stitch_preview->uvs_per_polygon); - stitch_preview->uvs_per_polygon = NULL; - } - if (stitch_preview->preview_stitchable) { - MEM_freeN(stitch_preview->preview_stitchable); - stitch_preview->preview_stitchable = NULL; - } - if (stitch_preview->preview_unstitchable) { - MEM_freeN(stitch_preview->preview_unstitchable); - stitch_preview->preview_unstitchable = NULL; - } - if (stitch_preview->static_tris) { - MEM_freeN(stitch_preview->static_tris); - stitch_preview->static_tris = NULL; - } + MEM_SAFE_FREE(stitch_preview->preview_polys); + MEM_SAFE_FREE(stitch_preview->uvs_per_polygon); + MEM_SAFE_FREE(stitch_preview->preview_stitchable); + MEM_SAFE_FREE(stitch_preview->preview_unstitchable); + MEM_SAFE_FREE(stitch_preview->static_tris); MEM_freeN(stitch_preview); } } @@ -1769,7 +1754,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void GPU_blend(GPU_BLEND_ALPHA); - /* Static Tris */ + /* Static Triangles. */ if (stitch_preview->static_tris) { UI_GetThemeColor4fv(TH_STITCH_PREVIEW_ACTIVE, col); vbo = GPU_vertbuf_create_with_format(&format); diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index db85292c6c0..2d0021a1fe8 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -266,7 +266,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject * cumap = (CurveMapping *)py_srna->ptr.data; BKE_curvemapping_init(cumap); /* disable extrapolation if enabled */ - if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) { + if (cumap->flag & CUMA_EXTEND_EXTRAPOLATE) { cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; BKE_curvemapping_changed(cumap, false); } diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index afb23690a84..b1aea8bf6cf 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -527,7 +527,7 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap, fe = fe->nextEdge(); continue; } - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility<G, I>( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -725,7 +725,7 @@ static void computeDetailedVisibility(ViewMap *ioViewMap, fe = fe->nextEdge(); continue; } - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility<G, I>( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -891,7 +891,7 @@ static void computeFastVisibility(ViewMap *ioViewMap, G &grid, real epsilon) continue; } if (even_test) { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { // ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility<G, I>( ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); @@ -1607,7 +1607,7 @@ void ViewMapBuilder::ComputeRayCastingVisibility(ViewMap *ioViewMap, real epsilo memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set<ViewShape *> occluders; do { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); #if LOGGING @@ -1763,7 +1763,7 @@ void ViewMapBuilder::ComputeFastRayCastingVisibility(ViewMap *ioViewMap, real ep fe = (*ve)->fedgeA(); do { if (even_test) { - if ((maxCard < qiMajority)) { + if (maxCard < qiMajority) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); // ARB: This is an error condition, not an alert condition. diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 809294ad274..f8d2acc74a8 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -33,9 +33,6 @@ set(SRC intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc - intern/multi_function_network.cc - intern/multi_function_network_evaluation.cc - intern/multi_function_network_optimization.cc FN_cpp_type.hh FN_cpp_type_make.hh @@ -49,9 +46,6 @@ set(SRC FN_multi_function_builder.hh FN_multi_function_context.hh FN_multi_function_data_type.hh - FN_multi_function_network.hh - FN_multi_function_network_evaluation.hh - FN_multi_function_network_optimization.hh FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_signature.hh @@ -68,7 +62,6 @@ if(WITH_GTESTS) tests/FN_cpp_type_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc - tests/FN_multi_function_network_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index bc3f398c8e9..7277bf99c12 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -70,64 +70,73 @@ #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" +/** + * Different types support different features. Features like copy constructability can be detected + * automatically easily. For some features this is harder as of C++17. Those have flags in this + * enum and need to be determined by the programmer. + */ +enum class CPPTypeFlags { + None = 0, + Hashable = 1 << 0, + Printable = 1 << 1, + EqualityComparable = 1 << 2, + + BasicType = Hashable | Printable | EqualityComparable, +}; +ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) + namespace blender::fn { -struct CPPTypeMembers { - int64_t size = 0; - int64_t alignment = 0; - uintptr_t alignment_mask = 0; - bool is_trivially_destructible = false; - bool has_special_member_functions = false; +/** Utility class to pass template parameters to constructor of `CPPType`. */ +template<typename T, CPPTypeFlags Flags> struct CPPTypeParam { +}; - void (*default_construct)(void *ptr) = nullptr; - void (*default_construct_indices)(void *ptr, IndexMask mask) = nullptr; +class CPPType : NonCopyable, NonMovable { + private: + int64_t size_ = 0; + int64_t alignment_ = 0; + uintptr_t alignment_mask_ = 0; + bool is_trivially_destructible_ = false; + bool has_special_member_functions_ = false; - void (*destruct)(void *ptr) = nullptr; - void (*destruct_indices)(void *ptr, IndexMask mask) = nullptr; + void (*default_construct_)(void *ptr) = nullptr; + void (*default_construct_indices_)(void *ptr, IndexMask mask) = nullptr; - void (*copy_assign)(const void *src, void *dst) = nullptr; - void (*copy_assign_indices)(const void *src, void *dst, IndexMask mask) = nullptr; + void (*destruct_)(void *ptr) = nullptr; + void (*destruct_indices_)(void *ptr, IndexMask mask) = nullptr; - void (*copy_construct)(const void *src, void *dst) = nullptr; - void (*copy_construct_indices)(const void *src, void *dst, IndexMask mask) = nullptr; + void (*copy_assign_)(const void *src, void *dst) = nullptr; + void (*copy_assign_indices_)(const void *src, void *dst, IndexMask mask) = nullptr; - void (*move_assign)(void *src, void *dst) = nullptr; - void (*move_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*copy_construct_)(const void *src, void *dst) = nullptr; + void (*copy_construct_indices_)(const void *src, void *dst, IndexMask mask) = nullptr; - void (*move_construct)(void *src, void *dst) = nullptr; - void (*move_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*move_assign_)(void *src, void *dst) = nullptr; + void (*move_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*relocate_assign)(void *src, void *dst) = nullptr; - void (*relocate_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*move_construct_)(void *src, void *dst) = nullptr; + void (*move_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*relocate_construct)(void *src, void *dst) = nullptr; - void (*relocate_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr; + void (*relocate_assign_)(void *src, void *dst) = nullptr; + void (*relocate_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*fill_assign_indices)(const void *value, void *dst, IndexMask mask) = nullptr; + void (*relocate_construct_)(void *src, void *dst) = nullptr; + void (*relocate_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr; - void (*fill_construct_indices)(const void *value, void *dst, IndexMask mask) = nullptr; + void (*fill_assign_indices_)(const void *value, void *dst, IndexMask mask) = nullptr; - void (*print)(const void *value, std::stringstream &ss) = nullptr; - bool (*is_equal)(const void *a, const void *b) = nullptr; - uint64_t (*hash)(const void *value) = nullptr; + void (*fill_construct_indices_)(const void *value, void *dst, IndexMask mask) = nullptr; - const void *default_value = nullptr; - std::string name; -}; + void (*print_)(const void *value, std::stringstream &ss) = nullptr; + bool (*is_equal_)(const void *a, const void *b) = nullptr; + uint64_t (*hash_)(const void *value) = nullptr; -class CPPType : NonCopyable, NonMovable { - private: - CPPTypeMembers m_; + const void *default_value_ = nullptr; + std::string debug_name_; public: - CPPType(CPPTypeMembers members) : m_(std::move(members)) - { - BLI_assert(is_power_of_2_i(m_.alignment)); - m_.alignment_mask = (uintptr_t)members.alignment - (uintptr_t)1; - m_.has_special_member_functions = (m_.default_construct && m_.copy_construct && - m_.copy_assign && m_.move_construct && m_.move_assign && - m_.destruct); - } + template<typename T, CPPTypeFlags Flags> CPPType(CPPTypeParam<T, Flags>, StringRef debug_name); + virtual ~CPPType() = default; /** * Two types only compare equal when their pointer is equal. No two instances of CPPType for the @@ -148,7 +157,11 @@ class CPPType : NonCopyable, NonMovable { * This only works for types that actually implement the template specialization using * `MAKE_CPP_TYPE`. */ - template<typename T> static const CPPType &get(); + template<typename T> static const CPPType &get() + { + return CPPType::get_impl<std::remove_cv_t<T>>(); + } + template<typename T> static const CPPType &get_impl(); /** * Returns the name of the type for debugging purposes. This name should not be used as @@ -156,7 +169,7 @@ class CPPType : NonCopyable, NonMovable { */ StringRefNull name() const { - return m_.name; + return debug_name_; } /** @@ -167,7 +180,7 @@ class CPPType : NonCopyable, NonMovable { */ int64_t size() const { - return m_.size; + return size_; } /** @@ -178,7 +191,7 @@ class CPPType : NonCopyable, NonMovable { */ int64_t alignment() const { - return m_.alignment; + return alignment_; } /** @@ -190,52 +203,52 @@ class CPPType : NonCopyable, NonMovable { */ bool is_trivially_destructible() const { - return m_.is_trivially_destructible; + return is_trivially_destructible_; } bool is_default_constructible() const { - return m_.default_construct != nullptr; + return default_construct_ != nullptr; } bool is_copy_constructible() const { - return m_.copy_assign != nullptr; + return copy_assign_ != nullptr; } bool is_move_constructible() const { - return m_.move_assign != nullptr; + return move_assign_ != nullptr; } bool is_destructible() const { - return m_.destruct != nullptr; + return destruct_ != nullptr; } bool is_copy_assignable() const { - return m_.copy_assign != nullptr; + return copy_assign_ != nullptr; } bool is_move_assignable() const { - return m_.copy_construct != nullptr; + return copy_construct_ != nullptr; } bool is_printable() const { - return m_.print != nullptr; + return print_ != nullptr; } bool is_equality_comparable() const { - return m_.is_equal != nullptr; + return is_equal_ != nullptr; } bool is_hashable() const { - return m_.hash != nullptr; + return hash_ != nullptr; } /** @@ -249,7 +262,7 @@ class CPPType : NonCopyable, NonMovable { */ bool has_special_member_functions() const { - return m_.has_special_member_functions; + return has_special_member_functions_; } /** @@ -257,7 +270,7 @@ class CPPType : NonCopyable, NonMovable { */ bool pointer_has_valid_alignment(const void *ptr) const { - return ((uintptr_t)ptr & m_.alignment_mask) == 0; + return ((uintptr_t)ptr & alignment_mask_) == 0; } bool pointer_can_point_to_instance(const void *ptr) const @@ -277,7 +290,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(ptr)); - m_.default_construct(ptr); + default_construct_(ptr); } void default_construct_n(void *ptr, int64_t n) const @@ -289,7 +302,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - m_.default_construct_indices(ptr, mask); + default_construct_indices_(ptr, mask); } /** @@ -304,7 +317,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(ptr)); - m_.destruct(ptr); + destruct_(ptr); } void destruct_n(void *ptr, int64_t n) const @@ -316,7 +329,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - m_.destruct_indices(ptr, mask); + destruct_indices_(ptr, mask); } /** @@ -331,7 +344,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.copy_assign(src, dst); + copy_assign_(src, dst); } void copy_assign_n(const void *src, void *dst, int64_t n) const @@ -345,7 +358,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.copy_assign_indices(src, dst, mask); + copy_assign_indices_(src, dst, mask); } /** @@ -362,7 +375,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.copy_construct(src, dst); + copy_construct_(src, dst); } void copy_construct_n(const void *src, void *dst, int64_t n) const @@ -376,7 +389,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.copy_construct_indices(src, dst, mask); + copy_construct_indices_(src, dst, mask); } /** @@ -393,7 +406,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.move_assign(src, dst); + move_assign_(src, dst); } void move_assign_n(void *src, void *dst, int64_t n) const @@ -407,7 +420,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.move_assign_indices(src, dst, mask); + move_assign_indices_(src, dst, mask); } /** @@ -424,7 +437,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.move_construct(src, dst); + move_construct_(src, dst); } void move_construct_n(void *src, void *dst, int64_t n) const @@ -438,7 +451,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.move_construct_indices(src, dst, mask); + move_construct_indices_(src, dst, mask); } /** @@ -455,7 +468,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.relocate_assign(src, dst); + relocate_assign_(src, dst); } void relocate_assign_n(void *src, void *dst, int64_t n) const @@ -469,7 +482,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.relocate_assign_indices(src, dst, mask); + relocate_assign_indices_(src, dst, mask); } /** @@ -486,7 +499,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); - m_.relocate_construct(src, dst); + relocate_construct_(src, dst); } void relocate_construct_n(void *src, void *dst, int64_t n) const @@ -500,7 +513,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.relocate_construct_indices(src, dst, mask); + relocate_construct_indices_(src, dst, mask); } /** @@ -518,7 +531,7 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.fill_assign_indices(value, dst, mask); + fill_assign_indices_(value, dst, mask); } /** @@ -536,13 +549,13 @@ class CPPType : NonCopyable, NonMovable { BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - m_.fill_construct_indices(value, dst, mask); + fill_construct_indices_(value, dst, mask); } void print(const void *value, std::stringstream &ss) const { BLI_assert(this->pointer_can_point_to_instance(value)); - m_.print(value, ss); + print_(value, ss); } std::string to_string(const void *value) const @@ -566,7 +579,7 @@ class CPPType : NonCopyable, NonMovable { { BLI_assert(this->pointer_can_point_to_instance(a)); BLI_assert(this->pointer_can_point_to_instance(b)); - return m_.is_equal(a, b); + return is_equal_(a, b); } bool is_equal_or_false(const void *a, const void *b) const @@ -580,7 +593,7 @@ class CPPType : NonCopyable, NonMovable { uint64_t hash(const void *value) const { BLI_assert(this->pointer_can_point_to_instance(value)); - return m_.hash(value); + return hash_(value); } uint64_t hash_or_fallback(const void *value, uint64_t fallback_hash) const @@ -597,7 +610,7 @@ class CPPType : NonCopyable, NonMovable { */ const void *default_value() const { - return m_.default_value; + return default_value_; } uint64_t hash() const @@ -605,12 +618,9 @@ class CPPType : NonCopyable, NonMovable { return get_default_hash(this); } - /** - * Low level access to the callbacks for this CPPType. - */ - const CPPTypeMembers &members() const + void (*destruct_fn() const)(void *) { - return m_; + return destruct_; } template<typename T> bool is() const diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/functions/FN_cpp_type_make.hh index b8e5373ccf7..088f6b469f4 100644 --- a/source/blender/functions/FN_cpp_type_make.hh +++ b/source/blender/functions/FN_cpp_type_make.hh @@ -185,100 +185,80 @@ template<typename T> uint64_t hash_cb(const void *value) } // namespace blender::fn::cpp_type_util -/** - * Different types support different features. Features like copy constructability can be detected - * automatically easily. For some features this is harder as of C++17. Those have flags in this - * enum and need to be determined by the programmer. - */ -enum class CPPTypeFlags { - None = 0, - Hashable = 1 << 0, - Printable = 1 << 1, - EqualityComparable = 1 << 2, - - BasicType = Hashable | Printable | EqualityComparable, -}; -ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) - namespace blender::fn { -template<typename T, CPPTypeFlags flags> -inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name) +template<typename T, CPPTypeFlags Flags> +CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name) { using namespace cpp_type_util; - CPPTypeMembers m; - m.name = name; - m.size = (int64_t)sizeof(T); - m.alignment = (int64_t)alignof(T); - m.is_trivially_destructible = std::is_trivially_destructible_v<T>; + debug_name_ = debug_name; + size_ = (int64_t)sizeof(T); + alignment_ = (int64_t)alignof(T); + is_trivially_destructible_ = std::is_trivially_destructible_v<T>; if constexpr (std::is_default_constructible_v<T>) { - m.default_construct = default_construct_cb<T>; - m.default_construct_indices = default_construct_indices_cb<T>; + default_construct_ = default_construct_cb<T>; + default_construct_indices_ = default_construct_indices_cb<T>; static T default_value; - m.default_value = (void *)&default_value; + default_value_ = (void *)&default_value; } if constexpr (std::is_destructible_v<T>) { - m.destruct = destruct_cb<T>; - m.destruct_indices = destruct_indices_cb<T>; + destruct_ = destruct_cb<T>; + destruct_indices_ = destruct_indices_cb<T>; } if constexpr (std::is_copy_assignable_v<T>) { - m.copy_assign = copy_assign_cb<T>; - m.copy_assign_indices = copy_assign_indices_cb<T>; + copy_assign_ = copy_assign_cb<T>; + copy_assign_indices_ = copy_assign_indices_cb<T>; } if constexpr (std::is_copy_constructible_v<T>) { - m.copy_construct = copy_construct_cb<T>; - m.copy_construct_indices = copy_construct_indices_cb<T>; + copy_construct_ = copy_construct_cb<T>; + copy_construct_indices_ = copy_construct_indices_cb<T>; } if constexpr (std::is_move_assignable_v<T>) { - m.move_assign = move_assign_cb<T>; - m.move_assign_indices = move_assign_indices_cb<T>; + move_assign_ = move_assign_cb<T>; + move_assign_indices_ = move_assign_indices_cb<T>; } if constexpr (std::is_move_constructible_v<T>) { - m.move_construct = move_construct_cb<T>; - m.move_construct_indices = move_construct_indices_cb<T>; + move_construct_ = move_construct_cb<T>; + move_construct_indices_ = move_construct_indices_cb<T>; } if constexpr (std::is_destructible_v<T>) { if constexpr (std::is_move_assignable_v<T>) { - m.relocate_assign = relocate_assign_cb<T>; - m.relocate_assign_indices = relocate_assign_indices_cb<T>; + relocate_assign_ = relocate_assign_cb<T>; + relocate_assign_indices_ = relocate_assign_indices_cb<T>; } if constexpr (std::is_move_constructible_v<T>) { - m.relocate_construct = relocate_construct_cb<T>; - m.relocate_construct_indices = relocate_construct_indices_cb<T>; + relocate_construct_ = relocate_construct_cb<T>; + relocate_construct_indices_ = relocate_construct_indices_cb<T>; } } if constexpr (std::is_copy_assignable_v<T>) { - m.fill_assign_indices = fill_assign_indices_cb<T>; + fill_assign_indices_ = fill_assign_indices_cb<T>; } if constexpr (std::is_copy_constructible_v<T>) { - m.fill_construct_indices = fill_construct_indices_cb<T>; + fill_construct_indices_ = fill_construct_indices_cb<T>; } - if constexpr ((bool)(flags & CPPTypeFlags::Hashable)) { - m.hash = hash_cb<T>; + if constexpr ((bool)(Flags & CPPTypeFlags::Hashable)) { + hash_ = hash_cb<T>; } - if constexpr ((bool)(flags & CPPTypeFlags::Printable)) { - m.print = print_cb<T>; + if constexpr ((bool)(Flags & CPPTypeFlags::Printable)) { + print_ = print_cb<T>; } - if constexpr ((bool)(flags & CPPTypeFlags::EqualityComparable)) { - m.is_equal = is_equal_cb<T>; + if constexpr ((bool)(Flags & CPPTypeFlags::EqualityComparable)) { + is_equal_ = is_equal_cb<T>; } - const CPPType *type = new CPPType(std::move(m)); - return std::unique_ptr<const CPPType>(type); + alignment_mask_ = (uintptr_t)alignment_ - (uintptr_t)1; + has_special_member_functions_ = (default_construct_ && copy_construct_ && copy_assign_ && + move_construct_ && move_assign_ && destruct_); } } // namespace blender::fn #define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME, FLAGS) \ - template<> const blender::fn::CPPType &blender::fn::CPPType::get<TYPE_NAME>() \ - { \ - static std::unique_ptr<const CPPType> cpp_type = \ - blender::fn::create_cpp_type<TYPE_NAME, FLAGS>(STRINGIFY(IDENTIFIER)); \ - return *cpp_type; \ - } \ - /* Support using `CPPType::get<const T>()`. Otherwise the caller would have to remove const. */ \ - template<> const blender::fn::CPPType &blender::fn::CPPType::get<const TYPE_NAME>() \ + template<> const blender::fn::CPPType &blender::fn::CPPType::get_impl<TYPE_NAME>() \ { \ - return blender::fn::CPPType::get<TYPE_NAME>(); \ + static CPPType cpp_type{blender::fn::CPPTypeParam<TYPE_NAME, FLAGS>(), \ + STRINGIFY(IDENTIFIER)}; \ + return cpp_type; \ } diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index eeba0c9dba2..179e85671f8 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -82,6 +82,8 @@ class GVectorArray : NonCopyable, NonMovable { void extend(IndexMask mask, const GVVectorArray &values); void extend(IndexMask mask, const GVectorArray &values); + void clear(IndexMask mask); + GMutableSpan operator[](int64_t index); GSpan operator[](int64_t index) const; diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index c9398ceb547..f429243e2de 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -129,7 +129,7 @@ class GVArray { } /* Same as `get_internal_single`, but `r_value` points to initialized memory. */ - void get_single_to_uninitialized(void *r_value) const + void get_internal_single_to_uninitialized(void *r_value) const { type_->default_construct(r_value); this->get_internal_single(r_value); diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh deleted file mode 100644 index b303589106a..00000000000 --- a/source/blender/functions/FN_multi_function_network.hh +++ /dev/null @@ -1,536 +0,0 @@ -/* - * 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 fn - * - * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The - * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function - * (which can be used in another network and so on). - * - * A MFNetwork is a graph data structure with two kinds of nodes: - * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to - * parameters of the referenced multi-function. - * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be - * used to represent node group inputs and outputs. - * - * Links represent data flow. Unlinked input sockets have no value. In order to execute a function - * node, all its inputs have to be connected to something. - * - * Links are only allowed between sockets with the exact same MFDataType. There are no implicit - * conversions. - * - * Every input and output parameter of a multi-function corresponds to exactly one input or output - * socket respectively. A multiple parameter belongs to exactly one input AND one output socket. - * - * There is an .to_dot() method that generates a graph in dot format for debugging purposes. - */ - -#include "FN_multi_function.hh" - -#include "BLI_vector_set.hh" - -namespace blender::fn { - -class MFNode; -class MFFunctionNode; -class MFDummyNode; -class MFSocket; -class MFInputSocket; -class MFOutputSocket; -class MFNetwork; - -class MFNode : NonCopyable, NonMovable { - protected: - MFNetwork *network_; - Span<MFInputSocket *> inputs_; - Span<MFOutputSocket *> outputs_; - bool is_dummy_; - int id_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - - MFNetwork &network(); - const MFNetwork &network() const; - - bool is_dummy() const; - bool is_function() const; - - MFDummyNode &as_dummy(); - const MFDummyNode &as_dummy() const; - - MFFunctionNode &as_function(); - const MFFunctionNode &as_function() const; - - MFInputSocket &input(int index); - const MFInputSocket &input(int index) const; - - MFOutputSocket &output(int index); - const MFOutputSocket &output(int index) const; - - Span<MFInputSocket *> inputs(); - Span<const MFInputSocket *> inputs() const; - - Span<MFOutputSocket *> outputs(); - Span<const MFOutputSocket *> outputs() const; - - bool has_unlinked_inputs() const; - - private: - void destruct_sockets(); -}; - -class MFFunctionNode : public MFNode { - private: - const MultiFunction *function_; - Span<int> input_param_indices_; - Span<int> output_param_indices_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - const MultiFunction &function() const; - - const MFInputSocket &input_for_param(int param_index) const; - const MFOutputSocket &output_for_param(int param_index) const; -}; - -class MFDummyNode : public MFNode { - protected: - StringRefNull name_; - MutableSpan<StringRefNull> input_names_; - MutableSpan<StringRefNull> output_names_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - Span<StringRefNull> input_names() const; - Span<StringRefNull> output_names() const; -}; - -class MFSocket : NonCopyable, NonMovable { - protected: - MFNode *node_; - bool is_output_; - int index_; - MFDataType data_type_; - int id_; - StringRefNull name_; - - friend MFNetwork; - - public: - StringRefNull name() const; - - int id() const; - int index() const; - - const MFDataType &data_type() const; - - MFNode &node(); - const MFNode &node() const; - - bool is_input() const; - bool is_output() const; - - MFInputSocket &as_input(); - const MFInputSocket &as_input() const; - - MFOutputSocket &as_output(); - const MFOutputSocket &as_output() const; -}; - -class MFInputSocket : public MFSocket { - private: - MFOutputSocket *origin_; - - friend MFNetwork; - - public: - MFOutputSocket *origin(); - const MFOutputSocket *origin() const; -}; - -class MFOutputSocket : public MFSocket { - private: - Vector<MFInputSocket *, 1> targets_; - - friend MFNetwork; - - public: - Span<MFInputSocket *> targets(); - Span<const MFInputSocket *> targets() const; -}; - -class MFNetwork : NonCopyable, NonMovable { - private: - LinearAllocator<> allocator_; - - VectorSet<MFFunctionNode *> function_nodes_; - VectorSet<MFDummyNode *> dummy_nodes_; - - Vector<MFNode *> node_or_null_by_id_; - Vector<MFSocket *> socket_or_null_by_id_; - - public: - MFNetwork() = default; - ~MFNetwork(); - - MFFunctionNode &add_function(const MultiFunction &function); - MFDummyNode &add_dummy(StringRef name, - Span<MFDataType> input_types, - Span<MFDataType> output_types, - Span<StringRef> input_names, - Span<StringRef> output_names); - void add_link(MFOutputSocket &from, MFInputSocket &to); - - MFOutputSocket &add_input(StringRef name, MFDataType data_type); - MFInputSocket &add_output(StringRef name, MFDataType data_type); - - void relink(MFOutputSocket &old_output, MFOutputSocket &new_output); - - void remove(MFNode &node); - void remove(Span<MFNode *> nodes); - - int socket_id_amount() const; - int node_id_amount() const; - - Span<MFDummyNode *> dummy_nodes(); - Span<MFFunctionNode *> function_nodes(); - - MFNode *node_or_null_by_id(int id); - const MFNode *node_or_null_by_id(int id) const; - - MFSocket *socket_or_null_by_id(int id); - const MFSocket *socket_or_null_by_id(int id) const; - - void find_dependencies(Span<const MFInputSocket *> sockets, - VectorSet<const MFOutputSocket *> &r_dummy_sockets, - VectorSet<const MFInputSocket *> &r_unlinked_inputs) const; - - bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const; - - std::string to_dot(Span<const MFNode *> marked_nodes = {}) const; -}; - -/* -------------------------------------------------------------------- - * MFNode inline methods. - */ - -inline StringRefNull MFNode::name() const -{ - if (is_dummy_) { - return this->as_dummy().name(); - } - else { - return this->as_function().name(); - } -} - -inline int MFNode::id() const -{ - return id_; -} - -inline MFNetwork &MFNode::network() -{ - return *network_; -} - -inline const MFNetwork &MFNode::network() const -{ - return *network_; -} - -inline bool MFNode::is_dummy() const -{ - return is_dummy_; -} - -inline bool MFNode::is_function() const -{ - return !is_dummy_; -} - -inline MFDummyNode &MFNode::as_dummy() -{ - BLI_assert(is_dummy_); - return static_cast<MFDummyNode &>(*this); -} - -inline const MFDummyNode &MFNode::as_dummy() const -{ - BLI_assert(is_dummy_); - return static_cast<const MFDummyNode &>(*this); -} - -inline MFFunctionNode &MFNode::as_function() -{ - BLI_assert(!is_dummy_); - return static_cast<MFFunctionNode &>(*this); -} - -inline const MFFunctionNode &MFNode::as_function() const -{ - BLI_assert(!is_dummy_); - return static_cast<const MFFunctionNode &>(*this); -} - -inline MFInputSocket &MFNode::input(int index) -{ - return *inputs_[index]; -} - -inline const MFInputSocket &MFNode::input(int index) const -{ - return *inputs_[index]; -} - -inline MFOutputSocket &MFNode::output(int index) -{ - return *outputs_[index]; -} - -inline const MFOutputSocket &MFNode::output(int index) const -{ - return *outputs_[index]; -} - -inline Span<MFInputSocket *> MFNode::inputs() -{ - return inputs_; -} - -inline Span<const MFInputSocket *> MFNode::inputs() const -{ - return inputs_; -} - -inline Span<MFOutputSocket *> MFNode::outputs() -{ - return outputs_; -} - -inline Span<const MFOutputSocket *> MFNode::outputs() const -{ - return outputs_; -} - -inline bool MFNode::has_unlinked_inputs() const -{ - for (const MFInputSocket *socket : inputs_) { - if (socket->origin() == nullptr) { - return true; - } - } - return false; -} - -/* -------------------------------------------------------------------- - * MFFunctionNode inline methods. - */ - -inline StringRefNull MFFunctionNode::name() const -{ - return function_->name(); -} - -inline const MultiFunction &MFFunctionNode::function() const -{ - return *function_; -} - -inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const -{ - return this->input(input_param_indices_.first_index(param_index)); -} - -inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const -{ - return this->output(output_param_indices_.first_index(param_index)); -} - -/* -------------------------------------------------------------------- - * MFDummyNode inline methods. - */ - -inline StringRefNull MFDummyNode::name() const -{ - return name_; -} - -inline Span<StringRefNull> MFDummyNode::input_names() const -{ - return input_names_; -} - -inline Span<StringRefNull> MFDummyNode::output_names() const -{ - return output_names_; -} - -/* -------------------------------------------------------------------- - * MFSocket inline methods. - */ - -inline StringRefNull MFSocket::name() const -{ - return name_; -} - -inline int MFSocket::id() const -{ - return id_; -} - -inline int MFSocket::index() const -{ - return index_; -} - -inline const MFDataType &MFSocket::data_type() const -{ - return data_type_; -} - -inline MFNode &MFSocket::node() -{ - return *node_; -} - -inline const MFNode &MFSocket::node() const -{ - return *node_; -} - -inline bool MFSocket::is_input() const -{ - return !is_output_; -} - -inline bool MFSocket::is_output() const -{ - return is_output_; -} - -inline MFInputSocket &MFSocket::as_input() -{ - BLI_assert(this->is_input()); - return static_cast<MFInputSocket &>(*this); -} - -inline const MFInputSocket &MFSocket::as_input() const -{ - BLI_assert(this->is_input()); - return static_cast<const MFInputSocket &>(*this); -} - -inline MFOutputSocket &MFSocket::as_output() -{ - BLI_assert(this->is_output()); - return static_cast<MFOutputSocket &>(*this); -} - -inline const MFOutputSocket &MFSocket::as_output() const -{ - BLI_assert(this->is_output()); - return static_cast<const MFOutputSocket &>(*this); -} - -/* -------------------------------------------------------------------- - * MFInputSocket inline methods. - */ - -inline MFOutputSocket *MFInputSocket::origin() -{ - return origin_; -} - -inline const MFOutputSocket *MFInputSocket::origin() const -{ - return origin_; -} - -/* -------------------------------------------------------------------- - * MFOutputSocket inline methods. - */ - -inline Span<MFInputSocket *> MFOutputSocket::targets() -{ - return targets_; -} - -inline Span<const MFInputSocket *> MFOutputSocket::targets() const -{ - return targets_; -} - -/* -------------------------------------------------------------------- - * MFNetwork inline methods. - */ - -inline Span<MFDummyNode *> MFNetwork::dummy_nodes() -{ - return dummy_nodes_; -} - -inline Span<MFFunctionNode *> MFNetwork::function_nodes() -{ - return function_nodes_; -} - -inline MFNode *MFNetwork::node_or_null_by_id(int id) -{ - return node_or_null_by_id_[id]; -} - -inline const MFNode *MFNetwork::node_or_null_by_id(int id) const -{ - return node_or_null_by_id_[id]; -} - -inline MFSocket *MFNetwork::socket_or_null_by_id(int id) -{ - return socket_or_null_by_id_[id]; -} - -inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const -{ - return socket_or_null_by_id_[id]; -} - -inline int MFNetwork::socket_id_amount() const -{ - return socket_or_null_by_id_.size(); -} - -inline int MFNetwork::node_id_amount() const -{ - return node_or_null_by_id_.size(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh deleted file mode 100644 index 17cffa406f7..00000000000 --- a/source/blender/functions/FN_multi_function_network_evaluation.hh +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 fn - */ - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -class MFNetworkEvaluationStorage; - -class MFNetworkEvaluator : public MultiFunction { - private: - MFSignature signature_; - Vector<const MFOutputSocket *> inputs_; - Vector<const MFInputSocket *> outputs_; - - public: - MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs); - - void call(IndexMask mask, MFParams params, MFContext context) const override; - - private: - using Storage = MFNetworkEvaluationStorage; - - void copy_inputs_to_storage(MFParams params, Storage &storage) const; - void copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const; - - void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const; - - void evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const; - - bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const; - - void initialize_remaining_outputs(MFParams params, - Storage &storage, - Span<const MFInputSocket *> remaining_outputs) const; -}; - -} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index e292d11def7..a480287d578 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -54,6 +54,11 @@ class MFParamsBuilder { MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); + template<typename T> void add_readonly_single_input_value(T value, StringRef expected_name = "") + { + T *value_ptr = &scope_.add_value<T>(std::move(value), __func__); + this->add_readonly_single_input(value_ptr, expected_name); + } template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "") { this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>( @@ -83,6 +88,12 @@ class MFParamsBuilder { this->add_readonly_vector_input( scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name); } + void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "") + { + this->add_readonly_vector_input( + scope_.construct<GVVectorArray_For_SingleGSpan>(__func__, single_vector, min_array_size_), + expected_name); + } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") { this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name); diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index 23309c9a5e6..d05948cc645 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -160,6 +160,21 @@ class MFSignatureBuilder { } } + void add(StringRef name, const MFParamType ¶m_type) + { + switch (param_type.interface_type()) { + case MFParamType::Input: + this->input(name, param_type.data_type()); + break; + case MFParamType::Mutable: + this->mutable_(name, param_type.data_type()); + break; + case MFParamType::Output: + this->output(name, param_type.data_type()); + break; + } + } + /* Context */ /** This indicates that the function accesses the context. This disables optimizations that diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc index 9556d24218e..ec95a283919 100644 --- a/source/blender/functions/intern/generic_vector_array.cc +++ b/source/blender/functions/intern/generic_vector_array.cc @@ -78,6 +78,15 @@ void GVectorArray::extend(IndexMask mask, const GVectorArray &values) this->extend(mask, virtual_values); } +void GVectorArray::clear(IndexMask mask) +{ + for (const int64_t i : mask) { + Item &item = items_[i]; + type_.destruct_n(item.start, item.length); + item.length = 0; + } +} + GMutableSpan GVectorArray::operator[](const int64_t index) { Item &item = items_[index]; diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc deleted file mode 100644 index b5c2c09a35a..00000000000 --- a/source/blender/functions/intern/multi_function_network.cc +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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 "BLI_dot_export.hh" -#include "BLI_stack.hh" - -#include "FN_multi_function_network.hh" - -namespace blender::fn { - -MFNetwork::~MFNetwork() -{ - for (MFFunctionNode *node : function_nodes_) { - node->destruct_sockets(); - node->~MFFunctionNode(); - } - for (MFDummyNode *node : dummy_nodes_) { - node->destruct_sockets(); - node->~MFDummyNode(); - } -} - -void MFNode::destruct_sockets() -{ - for (MFInputSocket *socket : inputs_) { - socket->~MFInputSocket(); - } - for (MFOutputSocket *socket : outputs_) { - socket->~MFOutputSocket(); - } -} - -/** - * Add a new function node to the network. The caller keeps the ownership of the function. The - * function should not be freed before the network. A reference to the new node is returned. The - * node is owned by the network. - */ -MFFunctionNode &MFNetwork::add_function(const MultiFunction &function) -{ - Vector<int, 16> input_param_indices, output_param_indices; - - for (int param_index : function.param_indices()) { - switch (function.param_type(param_index).interface_type()) { - case MFParamType::Input: { - input_param_indices.append(param_index); - break; - } - case MFParamType::Output: { - output_param_indices.append(param_index); - break; - } - case MFParamType::Mutable: { - input_param_indices.append(param_index); - output_param_indices.append(param_index); - break; - } - } - } - - MFFunctionNode &node = *allocator_.construct<MFFunctionNode>().release(); - function_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = false; - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - node.function_ = &function; - node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices); - node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices); - - node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>( - input_param_indices.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>( - output_param_indices.size()); - - for (int i : input_param_indices.index_range()) { - int param_index = input_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_input_or_mutable()); - - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = function.param_name(param_index); - socket.origin_ = nullptr; - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - for (int i : output_param_indices.index_range()) { - int param_index = output_param_indices[i]; - MFParamType param = function.param_type(param_index); - BLI_assert(param.is_output_or_mutable()); - - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = param.data_type(); - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = function.param_name(param_index); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - } - - return node; -} - -/** - * Add a dummy node with the given input and output sockets. - */ -MFDummyNode &MFNetwork::add_dummy(StringRef name, - Span<MFDataType> input_types, - Span<MFDataType> output_types, - Span<StringRef> input_names, - Span<StringRef> output_names) -{ - assert_same_size(input_types, input_names); - assert_same_size(output_types, output_names); - - MFDummyNode &node = *allocator_.construct<MFDummyNode>().release(); - dummy_nodes_.add_new(&node); - - node.network_ = this; - node.is_dummy_ = true; - node.name_ = allocator_.copy_string(name); - node.id_ = node_or_null_by_id_.append_and_get_index(&node); - - node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>( - input_types.size()); - node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>( - output_types.size()); - - node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size()); - node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size()); - - for (int i : input_types.index_range()) { - MFInputSocket &socket = *node.inputs_[i]; - socket.data_type_ = input_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = false; - socket.name_ = allocator_.copy_string(input_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.input_names_[i] = socket.name_; - } - - for (int i : output_types.index_range()) { - MFOutputSocket &socket = *node.outputs_[i]; - socket.data_type_ = output_types[i]; - socket.node_ = &node; - socket.index_ = i; - socket.is_output_ = true; - socket.name_ = allocator_.copy_string(output_names[i]); - socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket); - node.output_names_[i] = socket.name_; - } - - return node; -} - -/** - * Connect two sockets. This invokes undefined behavior if the sockets belong to different - * networks, the sockets have a different data type, or the `to` socket is connected to something - * else already. - */ -void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to) -{ - BLI_assert(to.origin_ == nullptr); - BLI_assert(from.node_->network_ == to.node_->network_); - BLI_assert(from.data_type_ == to.data_type_); - from.targets_.append(&to); - to.origin_ = &from; -} - -MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0); -} - -MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type) -{ - return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0); -} - -void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output) -{ - BLI_assert(&old_output != &new_output); - BLI_assert(old_output.data_type_ == new_output.data_type_); - for (MFInputSocket *input : old_output.targets()) { - input->origin_ = &new_output; - } - new_output.targets_.extend(old_output.targets_); - old_output.targets_.clear(); -} - -void MFNetwork::remove(MFNode &node) -{ - for (MFInputSocket *socket : node.inputs_) { - if (socket->origin_ != nullptr) { - socket->origin_->targets_.remove_first_occurrence_and_reorder(socket); - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - for (MFOutputSocket *socket : node.outputs_) { - for (MFInputSocket *other : socket->targets_) { - other->origin_ = nullptr; - } - socket_or_null_by_id_[socket->id_] = nullptr; - } - node.destruct_sockets(); - if (node.is_dummy()) { - MFDummyNode &dummy_node = node.as_dummy(); - dummy_node.~MFDummyNode(); - dummy_nodes_.remove_contained(&dummy_node); - } - else { - MFFunctionNode &function_node = node.as_function(); - function_node.~MFFunctionNode(); - function_nodes_.remove_contained(&function_node); - } - node_or_null_by_id_[node.id_] = nullptr; -} - -void MFNetwork::remove(Span<MFNode *> nodes) -{ - for (MFNode *node : nodes) { - this->remove(*node); - } -} - -void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets, - VectorSet<const MFOutputSocket *> &r_dummy_sockets, - VectorSet<const MFInputSocket *> &r_unlinked_inputs) const -{ - Set<const MFNode *> visited_nodes; - Stack<const MFInputSocket *> sockets_to_check; - sockets_to_check.push_multiple(sockets); - - while (!sockets_to_check.is_empty()) { - const MFInputSocket &socket = *sockets_to_check.pop(); - const MFOutputSocket *origin_socket = socket.origin(); - if (origin_socket == nullptr) { - r_unlinked_inputs.add(&socket); - continue; - } - - const MFNode &origin_node = origin_socket->node(); - - if (origin_node.is_dummy()) { - r_dummy_sockets.add(origin_socket); - continue; - } - - if (visited_nodes.add(&origin_node)) { - sockets_to_check.push_multiple(origin_node.inputs()); - } - } -} - -bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const -{ - VectorSet<const MFOutputSocket *> dummy_sockets; - VectorSet<const MFInputSocket *> unlinked_inputs; - this->find_dependencies(sockets, dummy_sockets, unlinked_inputs); - return dummy_sockets.size() + unlinked_inputs.size() > 0; -} - -std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const -{ - dot::DirectedGraph digraph; - digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); - - Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes; - - Vector<const MFNode *> all_nodes; - all_nodes.extend(function_nodes_.as_span().cast<const MFNode *>()); - all_nodes.extend(dummy_nodes_.as_span().cast<const MFNode *>()); - - for (const MFNode *node : all_nodes) { - dot::Node &dot_node = digraph.new_node(""); - - Vector<std::string> input_names, output_names; - for (const MFInputSocket *socket : node->inputs_) { - input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")"); - } - for (const MFOutputSocket *socket : node->outputs_) { - output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")"); - } - - dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names}; - dot_nodes.add_new(node, dot_node_ref); - } - - for (const MFDummyNode *node : dummy_nodes_) { - dot_nodes.lookup(node).node().set_background_color("#77EE77"); - } - for (const MFNode *node : marked_nodes) { - dot_nodes.lookup(node).node().set_background_color("#7777EE"); - } - - for (const MFNode *to_node : all_nodes) { - dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node); - - for (const MFInputSocket *to_socket : to_node->inputs_) { - const MFOutputSocket *from_socket = to_socket->origin_; - if (from_socket != nullptr) { - const MFNode *from_node = from_socket->node_; - dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node); - digraph.new_edge(from_dot_node.output(from_socket->index_), - to_dot_node.input(to_socket->index_)); - } - } - } - - return digraph.to_dot_string(); -} - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc deleted file mode 100644 index 9a0cb0c35ce..00000000000 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ /dev/null @@ -1,1083 +0,0 @@ -/* - * 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 fn - * - * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller - * multi-functions. When called, it traverses the underlying MFNetwork and executes the required - * function nodes. - * - * There are many possible approaches to evaluate a function network. The approach implemented - * below has the following features: - * - It does not use recursion. Those could become problematic with long node chains. - * - It can handle all existing parameter types (including mutable parameters). - * - Avoids data copies in many cases. - * - Every node is executed at most once. - * - Can compute sub-functions on a single element, when the result is the same for all elements. - * - * Possible improvements: - * - Cache and reuse buffers. - * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be - * computed. This reduces the number of required temporary buffers when they are reused. - */ - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_resource_scope.hh" -#include "BLI_stack.hh" - -namespace blender::fn { - -struct Value; - -/** - * This keeps track of all the values that flow through the multi-function network. Therefore it - * maintains a mapping between output sockets and their corresponding values. Every `value` - * references some memory, that is owned either by the caller or this storage. - * - * A value can be owned by different sockets over time to avoid unnecessary copies. - */ -class MFNetworkEvaluationStorage { - private: - LinearAllocator<> allocator_; - IndexMask mask_; - Array<Value *> value_per_output_id_; - int64_t min_array_size_; - - public: - MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount); - ~MFNetworkEvaluationStorage(); - - /* Add the values that have been provided by the caller of the multi-function network. */ - void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array); - void add_vector_input_from_caller(const MFOutputSocket &socket, - const GVVectorArray &virtual_vector_array); - void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span); - void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array); - - /* Get input buffers for function node evaluations. */ - const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope); - const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope); - - /* Get output buffers for function node evaluations. */ - GMutableSpan get_single_output__full(const MFOutputSocket &socket); - GMutableSpan get_single_output__single(const MFOutputSocket &socket); - GVectorArray &get_vector_output__full(const MFOutputSocket &socket); - GVectorArray &get_vector_output__single(const MFOutputSocket &socket); - - /* Get mutable buffers for function node evaluations. */ - GMutableSpan get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GMutableSpan get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - GVectorArray &get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope); - - /* Mark a node as being done with evaluation. This might free temporary buffers that are no - * longer needed. */ - void finish_node(const MFFunctionNode &node); - void finish_output_socket(const MFOutputSocket &socket); - void finish_input_socket(const MFInputSocket &socket); - - IndexMask mask() const; - bool socket_is_computed(const MFOutputSocket &socket); - bool is_same_value_for_every_index(const MFOutputSocket &socket); - bool socket_has_buffer_for_output(const MFOutputSocket &socket); -}; - -MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, - Vector<const MFInputSocket *> outputs) - : inputs_(std::move(inputs)), outputs_(std::move(outputs)) -{ - BLI_assert(outputs_.size() > 0); - MFSignatureBuilder signature{"Function Tree"}; - - for (const MFOutputSocket *socket : inputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_input(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_input(socket->name(), type.vector_base_type()); - break; - } - } - - for (const MFInputSocket *socket : outputs_) { - BLI_assert(socket->node().is_dummy()); - - MFDataType type = socket->data_type(); - switch (type.category()) { - case MFDataType::Single: - signature.single_output(socket->name(), type.single_type()); - break; - case MFDataType::Vector: - signature.vector_output(socket->name(), type.vector_base_type()); - break; - } - } - - signature_ = signature.build(); - this->set_signature(&signature_); -} - -void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const -{ - if (mask.size() == 0) { - return; - } - - const MFNetwork &network = outputs_[0]->node().network(); - Storage storage(mask, network.socket_id_amount()); - - Vector<const MFInputSocket *> outputs_to_initialize_in_the_end; - - this->copy_inputs_to_storage(params, storage); - this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end); - this->evaluate_network_to_compute_outputs(context, storage); - this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end); -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params, - Storage &storage) const -{ - for (int input_index : inputs_.index_range()) { - int param_index = input_index + 0; - const MFOutputSocket &socket = *inputs_[input_index]; - switch (socket.data_type().category()) { - case MFDataType::Single: { - const GVArray &input_list = params.readonly_single_input(param_index); - storage.add_single_input_from_caller(socket, input_list); - break; - } - case MFDataType::Vector: { - const GVVectorArray &input_list_list = params.readonly_vector_input(param_index); - storage.add_vector_input_from_caller(socket, input_list_list); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage( - MFParams params, - Storage &storage, - Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const -{ - for (int output_index : outputs_.index_range()) { - int param_index = output_index + inputs_.size(); - const MFInputSocket &socket = *outputs_[output_index]; - const MFOutputSocket &origin = *socket.origin(); - - if (origin.node().is_dummy()) { - BLI_assert(inputs_.contains(&origin)); - /* Don't overwrite input buffers. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - if (storage.socket_has_buffer_for_output(origin)) { - /* When two outputs will be initialized to the same values. */ - outputs_to_initialize_in_the_end.append(&socket); - continue; - } - - switch (socket.data_type().category()) { - case MFDataType::Single: { - GMutableSpan span = params.uninitialized_single_output(param_index); - storage.add_single_output_from_caller(origin, span); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.vector_output(param_index); - storage.add_vector_output_from_caller(origin, vector_array); - break; - } - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( - MFContext &global_context, Storage &storage) const -{ - Stack<const MFOutputSocket *, 32> sockets_to_compute; - for (const MFInputSocket *socket : outputs_) { - sockets_to_compute.push(socket->origin()); - } - - /* This is the main loop that traverses the MFNetwork. */ - while (!sockets_to_compute.is_empty()) { - const MFOutputSocket &socket = *sockets_to_compute.peek(); - const MFNode &node = socket.node(); - - if (storage.socket_is_computed(socket)) { - sockets_to_compute.pop(); - continue; - } - - BLI_assert(node.is_function()); - BLI_assert(!node.has_unlinked_inputs()); - const MFFunctionNode &function_node = node.as_function(); - - bool all_origins_are_computed = true; - for (const MFInputSocket *input_socket : function_node.inputs()) { - const MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - if (!storage.socket_is_computed(*origin)) { - sockets_to_compute.push(origin); - all_origins_are_computed = false; - } - } - } - - if (all_origins_are_computed) { - this->evaluate_function(global_context, function_node, storage); - sockets_to_compute.pop(); - } - } -} - -BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context, - const MFFunctionNode &function_node, - Storage &storage) const -{ - - const MultiFunction &function = function_node.function(); - // std::cout << "Function: " << function.name() << "\n"; - - if (this->can_do_single_value_evaluation(function_node, storage)) { - /* The function output would be the same for all elements. Therefore, it is enough to call the - * function only on a single element. This can avoid many duplicate computations. */ - MFParamsBuilder params{function, 1}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__single(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__single(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__single(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__single(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__single(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__single(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(IndexRange(1), params, global_context); - } - else { - MFParamsBuilder params{function, storage.mask().min_array_size()}; - ResourceScope &scope = params.resource_scope(); - - for (int param_index : function.param_indices()) { - MFParamType param_type = function.param_type(param_index); - switch (param_type.category()) { - case MFParamType::SingleInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVArray &values = storage.get_single_input__full(socket, scope); - params.add_readonly_single_input(values); - break; - } - case MFParamType::VectorInput: { - const MFInputSocket &socket = function_node.input_for_param(param_index); - const GVVectorArray &values = storage.get_vector_input__full(socket, scope); - params.add_readonly_vector_input(values); - break; - } - case MFParamType::SingleOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_single_output__full(socket); - params.add_uninitialized_single_output(values); - break; - } - case MFParamType::VectorOutput: { - const MFOutputSocket &socket = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_vector_output__full(socket); - params.add_vector_output(values); - break; - } - case MFParamType::SingleMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GMutableSpan values = storage.get_mutable_single__full(input, output, scope); - params.add_single_mutable(values); - break; - } - case MFParamType::VectorMutable: { - const MFInputSocket &input = function_node.input_for_param(param_index); - const MFOutputSocket &output = function_node.output_for_param(param_index); - GVectorArray &values = storage.get_mutable_vector__full(input, output, scope); - params.add_vector_mutable(values); - break; - } - } - } - - function.call(storage.mask(), params, global_context); - } - - storage.finish_node(function_node); -} - -bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node, - Storage &storage) const -{ - for (const MFInputSocket *socket : function_node.inputs()) { - if (!storage.is_same_value_for_every_index(*socket->origin())) { - return false; - } - } - if (storage.mask().min_array_size() >= 1) { - for (const MFOutputSocket *socket : function_node.outputs()) { - if (storage.socket_has_buffer_for_output(*socket)) { - return false; - } - } - } - return true; -} - -BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs( - MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const -{ - ResourceScope scope; - for (const MFInputSocket *socket : remaining_outputs) { - int param_index = inputs_.size() + outputs_.first_index_of(socket); - - switch (socket->data_type().category()) { - case MFDataType::Single: { - const GVArray &values = storage.get_single_input__full(*socket, scope); - GMutableSpan output_values = params.uninitialized_single_output(param_index); - values.materialize_to_uninitialized(storage.mask(), output_values.data()); - break; - } - case MFDataType::Vector: { - const GVVectorArray &values = storage.get_vector_input__full(*socket, scope); - GVectorArray &output_values = params.vector_output(param_index); - output_values.extend(storage.mask(), values); - break; - } - } - } -} - -/* -------------------------------------------------------------------- */ -/** \name Value Types - * \{ */ - -enum class ValueType { - InputSingle, - InputVector, - OutputSingle, - OutputVector, - OwnSingle, - OwnVector, -}; - -struct Value { - ValueType type; - - Value(ValueType type) : type(type) - { - } -}; - -struct InputSingleValue : public Value { - /** This virtual array has been provided by the code that called the multi-function network. */ - const GVArray &virtual_array; - - InputSingleValue(const GVArray &virtual_array) - : Value(ValueType::InputSingle), virtual_array(virtual_array) - { - } -}; - -struct InputVectorValue : public Value { - /** This virtual vector has been provided by the code that called the multi-function network. */ - const GVVectorArray &virtual_vector_array; - - InputVectorValue(const GVVectorArray &virtual_vector_array) - : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array) - { - } -}; - -struct OutputValue : public Value { - bool is_computed = false; - - OutputValue(ValueType type) : Value(type) - { - } -}; - -struct OutputSingleValue : public OutputValue { - /** This span has been provided by the code that called the multi-function network. */ - GMutableSpan span; - - OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span) - { - } -}; - -struct OutputVectorValue : public OutputValue { - /** This vector array has been provided by the code that called the multi-function network. */ - GVectorArray *vector_array; - - OutputVectorValue(GVectorArray &vector_array) - : OutputValue(ValueType::OutputVector), vector_array(&vector_array) - { - } -}; - -struct OwnSingleValue : public Value { - /** This span has been allocated during the evaluation of the multi-function network and contains - * intermediate data. It has to be freed once the network evaluation is finished. */ - GMutableSpan span; - int max_remaining_users; - bool is_single_allocated; - - OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated) - : Value(ValueType::OwnSingle), - span(span), - max_remaining_users(max_remaining_users), - is_single_allocated(is_single_allocated) - { - } -}; - -struct OwnVectorValue : public Value { - /** This vector array has been allocated during the evaluation of the multi-function network and - * contains intermediate data. It has to be freed once the network evaluation is finished. */ - GVectorArray *vector_array; - int max_remaining_users; - - OwnVectorValue(GVectorArray &vector_array, int max_remaining_users) - : Value(ValueType::OwnVector), - vector_array(&vector_array), - max_remaining_users(max_remaining_users) - { - } -}; - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Storage methods - * \{ */ - -MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount) - : mask_(mask), - value_per_output_id_(socket_id_amount, nullptr), - min_array_size_(mask.min_array_size()) -{ -} - -MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage() -{ - for (Value *any_value : value_per_output_id_) { - if (any_value == nullptr) { - continue; - } - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - } - else if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - delete value->vector_array; - } - } -} - -IndexMask MFNetworkEvaluationStorage::mask() const -{ - return mask_; -} - -bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - return static_cast<OutputValue *>(any_value)->is_computed; - } - return true; -} - -bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - switch (any_value->type) { - case ValueType::OwnSingle: - return static_cast<OwnSingleValue *>(any_value)->span.size() == 1; - case ValueType::OwnVector: - return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1; - case ValueType::InputSingle: - return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single(); - case ValueType::InputVector: - return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector(); - case ValueType::OutputSingle: - return static_cast<OutputSingleValue *>(any_value)->span.size() == 1; - case ValueType::OutputVector: - return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1; - } - BLI_assert(false); - return false; -} - -bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return false; - } - - BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)); - return true; -} - -void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node) -{ - for (const MFInputSocket *socket : node.inputs()) { - this->finish_input_socket(*socket); - } - for (const MFOutputSocket *socket : node.outputs()) { - this->finish_output_socket(*socket); - } -} - -void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - return; - } - - if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) { - static_cast<OutputValue *>(any_value)->is_computed = true; - } -} - -void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket) -{ - const MFOutputSocket &origin = *socket.origin(); - - Value *any_value = value_per_output_id_[origin.id()]; - if (any_value == nullptr) { - /* Can happen when a value has been forward to the next node. */ - return; - } - - switch (any_value->type) { - case ValueType::InputSingle: - case ValueType::OutputSingle: - case ValueType::InputVector: - case ValueType::OutputVector: { - break; - } - case ValueType::OwnSingle: { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - GMutableSpan span = value->span; - const CPPType &type = span.type(); - if (value->is_single_allocated) { - type.destruct(span.data()); - } - else { - type.destruct_indices(span.data(), mask_); - MEM_freeN(span.data()); - } - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - case ValueType::OwnVector: { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - BLI_assert(value->max_remaining_users >= 1); - value->max_remaining_users--; - if (value->max_remaining_users == 0) { - delete value->vector_array; - value_per_output_id_[origin.id()] = nullptr; - } - break; - } - } -} - -void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket, - const GVArray &virtual_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_array.size() >= min_array_size_); - - auto *value = allocator_.construct<InputSingleValue>(virtual_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_input_from_caller( - const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(virtual_vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket, - GMutableSpan span) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(span.size() >= min_array_size_); - - auto *value = allocator_.construct<OutputSingleValue>(span).release(); - value_per_output_id_[socket.id()] = value; -} - -void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket, - GVectorArray &vector_array) -{ - BLI_assert(value_per_output_id_[socket.id()] == nullptr); - BLI_assert(vector_array.size() >= min_array_size_); - - auto *value = allocator_.construct<OutputVectorValue>(vector_array).release(); - value_per_output_id_[socket.id()] = value; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan span(type, buffer, min_array_size_); - - auto *value = - allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release(); - value_per_output_id_[socket.id()] = value; - - return span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - return static_cast<OutputSingleValue *>(any_value)->span; -} - -GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().single_type(); - void *buffer = allocator_.allocate(type.size(), type.alignment()); - GMutableSpan span(type, buffer, 1); - - auto *value = - allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release(); - value_per_output_id_[socket.id()] = value; - - return value->span; - } - - BLI_assert(any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span; - BLI_assert(span.size() == 1); - return span; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, min_array_size_); - - auto *value = - allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - return *static_cast<OutputVectorValue *>(any_value)->vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket) -{ - Value *any_value = value_per_output_id_[socket.id()]; - if (any_value == nullptr) { - const CPPType &type = socket.data_type().vector_base_type(); - GVectorArray *vector_array = new GVectorArray(type, 1); - - auto *value = - allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release(); - value_per_output_id_[socket.id()] = value; - - return *value->vector_array; - } - - BLI_assert(any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - return vector_array; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; - const GVArray &virtual_array = this->get_single_input__full(input, scope); - virtual_array.materialize_to_uninitialized(mask_, span.data()); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value); - if (value->max_remaining_users == 1 && !value->is_single_allocated) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__full(input, scope); - void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT); - GMutableSpan new_array_ref(type, new_buffer, min_array_size_); - virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data()); - - OwnSingleValue *new_value = - allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &type = from.data_type().single_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(type == to.data_type().single_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputSingle); - GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span; - BLI_assert(span.size() == 1); - const GVArray &virtual_array = this->get_single_input__single(input, scope); - virtual_array.get_single_to_uninitialized(span[0]); - return span; - } - - if (from_any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - BLI_assert(value->span.size() == 1); - return value->span; - } - } - - const GVArray &virtual_array = this->get_single_input__single(input, scope); - - void *new_buffer = allocator_.allocate(type.size(), type.alignment()); - virtual_array.get_single_to_uninitialized(new_buffer); - GMutableSpan new_array_ref(type, new_buffer, 1); - - OwnSingleValue *new_value = - allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release(); - value_per_output_id_[to.id()] = new_value; - return new_array_ref; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - vector_array.extend(mask_, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_); - new_vector_array->extend(mask_, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - - return *new_vector_array; -} - -GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input, - const MFOutputSocket &output, - ResourceScope &scope) -{ - const MFOutputSocket &from = *input.origin(); - const MFOutputSocket &to = output; - const CPPType &base_type = from.data_type().vector_base_type(); - - Value *from_any_value = value_per_output_id_[from.id()]; - Value *to_any_value = value_per_output_id_[to.id()]; - BLI_assert(from_any_value != nullptr); - BLI_assert(base_type == to.data_type().vector_base_type()); - - if (to_any_value != nullptr) { - BLI_assert(to_any_value->type == ValueType::OutputVector); - GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array; - BLI_assert(vector_array.size() == 1); - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - vector_array.extend({0}, virtual_vector_array); - return vector_array; - } - - if (from_any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value); - if (value->max_remaining_users == 1) { - value_per_output_id_[to.id()] = value; - value_per_output_id_[from.id()] = nullptr; - value->max_remaining_users = to.targets().size(); - return *value->vector_array; - } - } - - const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope); - - GVectorArray *new_vector_array = new GVectorArray(base_type, 1); - new_vector_array->extend({0}, virtual_vector_array); - - OwnVectorValue *new_value = - allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release(); - value_per_output_id_[to.id()] = new_value; - return *new_vector_array; -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - if (value->is_single_allocated) { - return scope.construct<GVArray_For_SingleValueRef>( - __func__, value->span.type(), min_array_size_, value->span.data()); - } - - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); - BLI_assert(value->is_computed); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - - BLI_assert(false); - return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); -} - -const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket, - ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnSingle) { - OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value); - BLI_assert(value->span.size() == 1); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - if (any_value->type == ValueType::InputSingle) { - InputSingleValue *value = static_cast<InputSingleValue *>(any_value); - BLI_assert(value->virtual_array.is_single()); - return value->virtual_array; - } - if (any_value->type == ValueType::OutputSingle) { - OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value); - BLI_assert(value->is_computed); - BLI_assert(value->span.size() == 1); - return scope.construct<GVArray_For_GSpan>(__func__, value->span); - } - - BLI_assert(false); - return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>()); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - if (value->vector_array->size() == 1) { - GSpan span = (*value->vector_array)[0]; - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_); - } - - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); -} - -const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single( - const MFInputSocket &socket, ResourceScope &scope) -{ - const MFOutputSocket &origin = *socket.origin(); - Value *any_value = value_per_output_id_[origin.id()]; - BLI_assert(any_value != nullptr); - - if (any_value->type == ValueType::OwnVector) { - OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - if (any_value->type == ValueType::InputVector) { - InputVectorValue *value = static_cast<InputVectorValue *>(any_value); - BLI_assert(value->virtual_vector_array.is_single_vector()); - return value->virtual_vector_array; - } - if (any_value->type == ValueType::OutputVector) { - OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value); - BLI_assert(value->vector_array->size() == 1); - return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array); - } - - BLI_assert(false); - return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0); -} - -/** \} */ - -} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc deleted file mode 100644 index 0f65d320f62..00000000000 --- a/source/blender/functions/intern/multi_function_network_optimization.cc +++ /dev/null @@ -1,501 +0,0 @@ -/* - * 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 fn - */ - -/* Used to check if two multi-functions have the exact same type. */ -#include <typeinfo> - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network_evaluation.hh" -#include "FN_multi_function_network_optimization.hh" - -#include "BLI_disjoint_set.hh" -#include "BLI_ghash.h" -#include "BLI_map.hh" -#include "BLI_multi_value_map.hh" -#include "BLI_rand.h" -#include "BLI_stack.hh" - -namespace blender::fn::mf_network_optimization { - -/* -------------------------------------------------------------------- */ -/** \name Utility functions to find nodes in a network. - * \{ */ - -static bool set_tag_and_check_if_modified(bool &tag, bool new_value) -{ - if (tag != new_value) { - tag = new_value; - return true; - } - - return false; -} - -static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes) -{ - Array<bool> is_to_the_left(network.node_id_amount(), false); - Stack<MFNode *> nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_left[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin = input_socket->origin(); - if (origin != nullptr) { - MFNode &origin_node = origin->node(); - if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) { - nodes_to_check.push(&origin_node); - } - } - } - } - - return is_to_the_left; -} - -static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes) -{ - Array<bool> is_to_the_right(network.node_id_amount(), false); - Stack<MFNode *> nodes_to_check; - - for (MFNode *node : nodes) { - is_to_the_right[node->id()] = true; - nodes_to_check.push(node); - } - - while (!nodes_to_check.is_empty()) { - MFNode &node = *nodes_to_check.pop(); - - for (MFOutputSocket *output_socket : node.outputs()) { - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) { - nodes_to_check.push(&target_node); - } - } - } - } - - return is_to_the_right; -} - -static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network, - Span<bool> id_mask, - bool mask_value) -{ - Vector<MFNode *> nodes; - for (int id : id_mask.index_range()) { - if (id_mask[id] == mask_value) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - nodes.append(node); - } - } - } - return nodes; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Dead Node Removal - * \{ */ - -/** - * Unused nodes are all those nodes that no dummy node depends upon. - */ -void dead_node_removal(MFNetwork &network) -{ - Array<bool> node_is_used_mask = mask_nodes_to_the_left(network, - network.dummy_nodes().cast<MFNode *>()); - Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false); - network.remove(nodes_to_remove); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Constant Folding - * \{ */ - -static bool function_node_can_be_constant(MFFunctionNode *node) -{ - if (node->has_unlinked_inputs()) { - return false; - } - if (node->function().depends_on_context()) { - return false; - } - return true; -} - -static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network) -{ - Vector<MFNode *> non_constant_nodes; - non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>()); - - for (MFFunctionNode *node : network.function_nodes()) { - if (!function_node_can_be_constant(node)) { - non_constant_nodes.append(node); - } - } - return non_constant_nodes; -} - -static bool output_has_non_constant_target_node(MFOutputSocket *output_socket, - Span<bool> is_not_constant_mask) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - MFNode &target_node = target_socket->node(); - bool target_is_not_constant = is_not_constant_mask[target_node.id()]; - if (target_is_not_constant) { - return true; - } - } - return false; -} - -static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket) -{ - for (MFInputSocket *target_socket : output_socket->targets()) { - if (target_socket->node().is_dummy()) { - return target_socket; - } - } - return nullptr; -} - -static Vector<MFInputSocket *> find_constant_inputs_to_fold( - MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes) -{ - Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network); - Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes); - Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false); - - Vector<MFInputSocket *> sockets_to_compute; - for (MFNode *node : constant_nodes) { - if (node->inputs().size() == 0) { - continue; - } - - for (MFOutputSocket *output_socket : node->outputs()) { - MFDataType data_type = output_socket->data_type(); - if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) { - MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket); - if (dummy_target == nullptr) { - dummy_target = &network.add_output("Dummy", data_type); - network.add_link(*output_socket, *dummy_target); - r_temporary_nodes.append(&dummy_target->node().as_dummy()); - } - - sockets_to_compute.append(dummy_target); - } - } - } - return sockets_to_compute; -} - -static void prepare_params_for_constant_folding(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope) -{ - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - switch (data_type.category()) { - case MFDataType::Single: { - /* Allocates memory for a single constant folded value. */ - const CPPType &cpp_type = data_type.single_type(); - void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment()); - GMutableSpan array{cpp_type, buffer, 1}; - params.add_uninitialized_single_output(array); - break; - } - case MFDataType::Vector: { - /* Allocates memory for a constant folded vector. */ - const CPPType &cpp_type = data_type.vector_base_type(); - GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1); - params.add_vector_output(vector_array); - break; - } - } - } -} - -static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn, - MFParamsBuilder ¶ms, - ResourceScope &scope, - MFNetwork &network) -{ - Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr}; - - for (int param_index : network_fn.param_indices()) { - MFParamType param_type = network_fn.param_type(param_index); - MFDataType data_type = param_type.data_type(); - - const MultiFunction *constant_fn = nullptr; - - switch (data_type.category()) { - case MFDataType::Single: { - const CPPType &cpp_type = data_type.single_type(); - GMutableSpan array = params.computed_array(param_index); - void *buffer = array.data(); - scope.add(buffer, array.type().members().destruct, AT); - - constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); - break; - } - case MFDataType::Vector: { - GVectorArray &vector_array = params.computed_vector_array(param_index); - GSpan array = vector_array[0]; - constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array); - break; - } - } - - MFFunctionNode &folded_node = network.add_function(*constant_fn); - folded_sockets[param_index] = &folded_node.output(0); - } - return folded_sockets; -} - -static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes( - MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope) -{ - MFNetworkEvaluator network_fn{{}, sockets_to_compute}; - - MFContextBuilder context; - MFParamsBuilder params{network_fn, 1}; - prepare_params_for_constant_folding(network_fn, params, scope); - network_fn.call({0}, params, context); - return add_constant_folded_sockets(network_fn, params, scope, network); -} - -class MyClass { - MFDummyNode node; -}; - -/** - * Find function nodes that always output the same value and replace those with constant nodes. - */ -void constant_folding(MFNetwork &network, ResourceScope &scope) -{ - Vector<MFDummyNode *> temporary_nodes; - Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes); - if (inputs_to_fold.size() == 0) { - return; - } - - Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes( - network, inputs_to_fold, scope); - - for (int i : inputs_to_fold.index_range()) { - MFOutputSocket &original_socket = *inputs_to_fold[i]->origin(); - network.relink(original_socket, *folded_sockets[i]); - } - - network.remove(temporary_nodes.as_span().cast<MFNode *>()); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Common Sub-network Elimination - * \{ */ - -static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes) -{ - if (node.function().depends_on_context()) { - return BLI_rng_get_uint(rng); - } - if (node.has_unlinked_inputs()) { - return BLI_rng_get_uint(rng); - } - - uint64_t combined_inputs_hash = 394659347u; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], - origin_socket->index()); - combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash); - } - - uint64_t function_hash = node.function().hash(); - uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash); - return node_hash; -} - -/** - * Produces a hash for every node. Two nodes with the same hash should have a high probability of - * outputting the same values. - */ -static Array<uint64_t> compute_node_hashes(MFNetwork &network) -{ - RNG *rng = BLI_rng_new(0); - Array<uint64_t> node_hashes(network.node_id_amount()); - Array<bool> node_is_hashed(network.node_id_amount(), false); - - /* No dummy nodes are not assumed to output the same values. */ - for (MFDummyNode *node : network.dummy_nodes()) { - uint64_t node_hash = BLI_rng_get_uint(rng); - node_hashes[node->id()] = node_hash; - node_is_hashed[node->id()] = true; - } - - Stack<MFFunctionNode *> nodes_to_check; - nodes_to_check.push_multiple(network.function_nodes()); - - while (!nodes_to_check.is_empty()) { - MFFunctionNode &node = *nodes_to_check.peek(); - if (node_is_hashed[node.id()]) { - nodes_to_check.pop(); - continue; - } - - /* Make sure that origin nodes are hashed first. */ - bool all_dependencies_ready = true; - for (MFInputSocket *input_socket : node.inputs()) { - MFOutputSocket *origin_socket = input_socket->origin(); - if (origin_socket != nullptr) { - MFNode &origin_node = origin_socket->node(); - if (!node_is_hashed[origin_node.id()]) { - all_dependencies_ready = false; - nodes_to_check.push(&origin_node.as_function()); - } - } - } - if (!all_dependencies_ready) { - continue; - } - - uint64_t node_hash = compute_node_hash(node, rng, node_hashes); - node_hashes[node.id()] = node_hash; - node_is_hashed[node.id()] = true; - nodes_to_check.pop(); - } - - BLI_rng_free(rng); - return node_hashes; -} - -static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network, - Span<uint64_t> node_hashes) -{ - MultiValueMap<uint64_t, MFNode *> nodes_by_hash; - for (int id : IndexRange(network.node_id_amount())) { - MFNode *node = network.node_or_null_by_id(id); - if (node != nullptr) { - uint64_t node_hash = node_hashes[id]; - nodes_by_hash.add(node_hash, node); - } - } - return nodes_by_hash; -} - -static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b) -{ - if (&a == &b) { - return true; - } - if (typeid(a) == typeid(b)) { - return a.equals(b); - } - return false; -} - -static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b) -{ - if (cache.in_same_set(a.id(), b.id())) { - return true; - } - - if (a.is_dummy() || b.is_dummy()) { - return false; - } - if (!functions_are_equal(a.as_function().function(), b.as_function().function())) { - return false; - } - for (int i : a.inputs().index_range()) { - const MFOutputSocket *origin_a = a.input(i).origin(); - const MFOutputSocket *origin_b = b.input(i).origin(); - if (origin_a == nullptr || origin_b == nullptr) { - return false; - } - if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) { - return false; - } - } - - cache.join(a.id(), b.id()); - return true; -} - -static void relink_duplicate_nodes(MFNetwork &network, - MultiValueMap<uint64_t, MFNode *> &nodes_by_hash) -{ - DisjointSet same_node_cache{network.node_id_amount()}; - - for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) { - if (nodes_with_same_hash.size() <= 1) { - continue; - } - - Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash; - while (nodes_to_check.size() >= 2) { - Vector<MFNode *, 16> remaining_nodes; - - MFNode &deduplicated_node = *nodes_to_check[0]; - for (MFNode *node : nodes_to_check.as_span().drop_front(1)) { - /* This is true with fairly high probability, but hash collisions can happen. So we have to - * check if the node actually output the same values. */ - if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) { - for (int i : deduplicated_node.outputs().index_range()) { - network.relink(node->output(i), deduplicated_node.output(i)); - } - } - else { - remaining_nodes.append(node); - } - } - nodes_to_check = std::move(remaining_nodes); - } - } -} - -/** - * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node - * groups were used to create the network. - */ -void common_subnetwork_elimination(MFNetwork &network) -{ - Array<uint64_t> node_hashes = compute_node_hashes(network); - MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes); - relink_duplicate_nodes(network, nodes_by_hash); -} - -/** \} */ - -} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc deleted file mode 100644 index 7b9738e5ca4..00000000000 --- a/source/blender/functions/tests/FN_multi_function_network_test.cc +++ /dev/null @@ -1,280 +0,0 @@ -/* Apache License, Version 2.0 */ - -#include "testing/testing.h" - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" -#include "FN_multi_function_network_evaluation.hh" - -namespace blender::fn::tests { -namespace { - -TEST(multi_function_network, Test1) -{ - CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; }); - CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; }); - - MFNetwork network; - - MFNode &node1 = network.add_function(add_10_fn); - MFNode &node2 = network.add_function(multiply_fn); - MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>()); - MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>()); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node1.output(0), node2.input(1)); - network.add_link(node2.output(0), output_socket); - network.add_link(input_socket, node1.input(0)); - - MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}}; - - { - Array<int> values = {4, 6, 1, 2, 0}; - Array<int> results(values.size(), 0); - - MFParamsBuilder params(network_fn, values.size()); - params.add_readonly_single_input(values.as_span()); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 2, 3, 4}, params, context); - - EXPECT_EQ(results[0], 14 * 14); - EXPECT_EQ(results[1], 0); - EXPECT_EQ(results[2], 11 * 11); - EXPECT_EQ(results[3], 12 * 12); - EXPECT_EQ(results[4], 10 * 10); - } - { - int value = 3; - Array<int> results(5, 0); - - MFParamsBuilder params(network_fn, results.size()); - params.add_readonly_single_input(&value); - params.add_uninitialized_single_output(results.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(results[0], 0); - EXPECT_EQ(results[1], 13 * 13); - EXPECT_EQ(results[2], 13 * 13); - EXPECT_EQ(results[3], 0); - EXPECT_EQ(results[4], 13 * 13); - } -} - -class ConcatVectorsFunction : public MultiFunction { - public: - ConcatVectorsFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Concat Vectors"}; - signature.vector_mutable<int>("A"); - signature.vector_input<int>("B"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &a = params.vector_mutable(0); - const GVVectorArray &b = params.readonly_vector_input(1); - a.extend(mask, b); - } -}; - -class AppendFunction : public MultiFunction { - public: - AppendFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable<int>("Vector"); - signature.single_input<int>("Value"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0); - const VArray<int> &values = params.readonly_single_input<int>(1); - - for (int64_t i : mask) { - vectors.append(i, values[i]); - } - } -}; - -class SumVectorFunction : public MultiFunction { - public: - SumVectorFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Sum Vectors"}; - signature.vector_input<int>("Vector"); - signature.single_output<int>("Sum"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0); - MutableSpan<int> sums = params.uninitialized_single_output<int>(1); - - for (int64_t i : mask) { - int sum = 0; - for (int j : IndexRange(vectors.get_vector_size(i))) { - sum += vectors.get_vector_element(i, j); - } - sums[i] = sum; - } - } -}; - -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input<int>("Size"); - signature.vector_output<int>("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size"); - GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range"); - - for (int64_t i : mask) { - int size = sizes[i]; - for (int j : IndexRange(size)) { - ranges.append(i, j); - } - } - } -}; - -TEST(multi_function_network, Test2) -{ - CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; }); - - ConcatVectorsFunction concat_vectors_fn; - AppendFunction append_fn; - SumVectorFunction sum_fn; - CreateRangeFunction create_range_fn; - - MFNetwork network; - - MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>()); - MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>()); - MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>()); - MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>()); - - MFNode &node1 = network.add_function(add_3_fn); - MFNode &node2 = network.add_function(create_range_fn); - MFNode &node3 = network.add_function(concat_vectors_fn); - MFNode &node4 = network.add_function(sum_fn); - MFNode &node5 = network.add_function(append_fn); - MFNode &node6 = network.add_function(sum_fn); - - network.add_link(input2, node1.input(0)); - network.add_link(node1.output(0), node2.input(0)); - network.add_link(node2.output(0), node3.input(1)); - network.add_link(input1, node3.input(0)); - network.add_link(input1, node4.input(0)); - network.add_link(node4.output(0), node5.input(1)); - network.add_link(node3.output(0), node5.input(0)); - network.add_link(node5.output(0), node6.input(0)); - network.add_link(node3.output(0), output1); - network.add_link(node6.output(0), output2); - - // std::cout << network.to_dot() << "\n\n"; - - MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}}; - - { - Array<int> input_value_1 = {3, 6}; - int input_value_2 = 4; - - GVectorArray output_value_1(CPPType::get<int32_t>(), 5); - Array<int> output_value_2(5, -1); - - MFParamsBuilder params(network_fn, 5); - GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5}; - params.add_readonly_vector_input(inputs_1); - params.add_readonly_single_input(&input_value_2); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({1, 2, 4}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 0); - EXPECT_EQ(output_value_1[1].size(), 9); - EXPECT_EQ(output_value_1[2].size(), 9); - EXPECT_EQ(output_value_1[3].size(), 0); - EXPECT_EQ(output_value_1[4].size(), 9); - - EXPECT_EQ(output_value_2[0], -1); - EXPECT_EQ(output_value_2[1], 39); - EXPECT_EQ(output_value_2[2], 39); - EXPECT_EQ(output_value_2[3], -1); - EXPECT_EQ(output_value_2[4], 39); - } - { - GVectorArray input_value_1(CPPType::get<int32_t>(), 3); - GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1}; - input_value_1_ref.extend(0, {3, 4, 5}); - input_value_1_ref.extend(1, {1, 2}); - - Array<int> input_value_2 = {4, 2, 3}; - - GVectorArray output_value_1(CPPType::get<int32_t>(), 3); - Array<int> output_value_2(3, -1); - - MFParamsBuilder params(network_fn, 3); - params.add_readonly_vector_input(input_value_1); - params.add_readonly_single_input(input_value_2.as_span()); - params.add_vector_output(output_value_1); - params.add_uninitialized_single_output(output_value_2.as_mutable_span()); - - MFContextBuilder context; - - network_fn.call({0, 1, 2}, params, context); - - EXPECT_EQ(output_value_1[0].size(), 10); - EXPECT_EQ(output_value_1[1].size(), 7); - EXPECT_EQ(output_value_1[2].size(), 6); - - EXPECT_EQ(output_value_2[0], 45); - EXPECT_EQ(output_value_2[1], 16); - EXPECT_EQ(output_value_2[2], 15); - } -} - -} // namespace -} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 3d73e020eb2..91c72a51dd6 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -4,6 +4,7 @@ #include "FN_multi_function.hh" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" namespace blender::fn::tests { namespace { @@ -59,33 +60,6 @@ TEST(multi_function, AddFunction) EXPECT_EQ(output[2], 36); } -class AddPrefixFunction : public MultiFunction { - public: - AddPrefixFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Add Prefix"}; - signature.single_input<std::string>("Prefix"); - signature.single_mutable<std::string>("Strings"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix"); - MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); - - for (int64_t i : mask) { - strings[i] = prefixes[i] + strings[i]; - } - } -}; - TEST(multi_function, AddPrefixFunction) { AddPrefixFunction fn; @@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction) EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation"); } -class CreateRangeFunction : public MultiFunction { - public: - CreateRangeFunction() - { - static MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static MFSignature create_signature() - { - MFSignatureBuilder signature{"Create Range"}; - signature.single_input<uint>("Size"); - signature.vector_output<uint>("Range"); - return signature.build(); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size"); - GVectorArray &ranges = params.vector_output(1, "Range"); - - for (int64_t i : mask) { - uint size = sizes[i]; - for (uint j : IndexRange(size)) { - ranges.append(i, &j); - } - } - } -}; - TEST(multi_function, CreateRangeFunction) { CreateRangeFunction fn; - GVectorArray ranges(CPPType::get<uint>(), 5); - GVectorArray_TypedMutableRef<uint> ranges_ref{ranges}; - Array<uint> sizes = {3, 0, 6, 1, 4}; + GVectorArray ranges(CPPType::get<int>(), 5); + GVectorArray_TypedMutableRef<int> ranges_ref{ranges}; + Array<int> sizes = {3, 0, 6, 1, 4}; MFParamsBuilder params(fn, ranges.size()); params.add_readonly_single_input(sizes.as_span()); @@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction) EXPECT_EQ(ranges_ref[2][1], 1); } -class GenericAppendFunction : public MultiFunction { - private: - MFSignature signature_; - - public: - GenericAppendFunction(const CPPType &type) - { - MFSignatureBuilder signature{"Append"}; - signature.vector_mutable("Vector", type); - signature.single_input("Value", type); - signature_ = signature.build(); - this->set_signature(&signature_); - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - GVectorArray &vectors = params.vector_mutable(0, "Vector"); - const GVArray &values = params.readonly_single_input(1, "Value"); - - for (int64_t i : mask) { - BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); - values.get(i, buffer); - vectors.append(i, buffer); - values.type().destruct(buffer); - } - } -}; - TEST(multi_function, GenericAppendFunction) { GenericAppendFunction fn(CPPType::get<int32_t>()); diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh new file mode 100644 index 00000000000..51c8fac8a96 --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -0,0 +1,174 @@ +/* Apache License, Version 2.0 */ + +#include "FN_multi_function.hh" + +namespace blender::fn::tests { + +class AddPrefixFunction : public MultiFunction { + public: + AddPrefixFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Add Prefix"}; + signature.single_input<std::string>("Prefix"); + signature.single_mutable<std::string>("Strings"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix"); + MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings"); + + for (int64_t i : mask) { + strings[i] = prefixes[i] + strings[i]; + } + } +}; + +class CreateRangeFunction : public MultiFunction { + public: + CreateRangeFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Create Range"}; + signature.single_input<int>("Size"); + signature.vector_output<int>("Range"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size"); + GVectorArray &ranges = params.vector_output(1, "Range"); + + for (int64_t i : mask) { + int size = sizes[i]; + for (int j : IndexRange(size)) { + ranges.append(i, &j); + } + } + } +}; + +class GenericAppendFunction : public MultiFunction { + private: + MFSignature signature_; + + public: + GenericAppendFunction(const CPPType &type) + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable("Vector", type); + signature.single_input("Value", type); + signature_ = signature.build(); + this->set_signature(&signature_); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &vectors = params.vector_mutable(0, "Vector"); + const GVArray &values = params.readonly_single_input(1, "Value"); + + for (int64_t i : mask) { + BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer); + values.get(i, buffer); + vectors.append(i, buffer); + values.type().destruct(buffer); + } + } +}; + +class ConcatVectorsFunction : public MultiFunction { + public: + ConcatVectorsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Concat Vectors"}; + signature.vector_mutable<int>("A"); + signature.vector_input<int>("B"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray &a = params.vector_mutable(0); + const GVVectorArray &b = params.readonly_vector_input(1); + a.extend(mask, b); + } +}; + +class AppendFunction : public MultiFunction { + public: + AppendFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Append"}; + signature.vector_mutable<int>("Vector"); + signature.single_input<int>("Value"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0); + const VArray<int> &values = params.readonly_single_input<int>(1); + + for (int64_t i : mask) { + vectors.append(i, values[i]); + } + } +}; + +class SumVectorFunction : public MultiFunction { + public: + SumVectorFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Sum Vectors"}; + signature.vector_input<int>("Vector"); + signature.single_output<int>("Sum"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0); + MutableSpan<int> sums = params.uninitialized_single_output<int>(1); + + for (int64_t i : mask) { + int sum = 0; + for (int j : IndexRange(vectors.get_vector_size(i))) { + sum += vectors.get_vector_element(i, j); + } + sums[i] = sum; + } + } +}; + +} // namespace blender::fn::tests diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index fd94ac92bc3..857c683d95a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * 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 @@ -12,14 +10,11 @@ * 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, + * 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) 2017, Blender Foundation * This is a new part of Blender - * - * ***** END GPL LICENSE BLOCK ***** - * */ /** \file diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 680f5ab05ec..7ce731c0dd6 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -308,10 +308,7 @@ static void bakeModifier(Main *UNUSED(bmain), static void freeData(GpencilModifierData *md) { TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; - if (mmd->colorband) { - MEM_freeN(mmd->colorband); - mmd->colorband = NULL; - } + MEM_SAFE_FREE(mmd->colorband); if (mmd->curve_intensity) { BKE_curvemapping_free(mmd->curve_intensity); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 9ac07b9632d..4b71011b99a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -208,7 +208,7 @@ typedef struct LineartChainRegisterEntry { enum eLineArtTileRecursiveLimit { /* If tile gets this small, it's already much smaller than a pixel. No need to continue * splitting. */ - LRT_TILE_RECURSIVE_PERSPECTIVE = 30, + LRT_TILE_RECURSIVE_PERSPECTIVE = 16, /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */ LRT_TILE_RECURSIVE_ORTHO = 10, }; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 8762ca1f384..99e3d59a57f 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -41,6 +41,7 @@ #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_object.h" @@ -1691,8 +1692,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu } if (obi->free_use_mesh) { - BKE_mesh_free(obi->original_me); - MEM_freeN(obi->original_me); + BKE_id_free(NULL, &obi->original_me); } if (rb->remove_doubles) { @@ -1968,8 +1968,8 @@ static int lineart_usage_check(Collection *c, Object *ob, bool is_render) if (c->gobject.first) { if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) { - if ((is_render && (c->flag & COLLECTION_RESTRICT_RENDER)) || - ((!is_render) && (c->flag & COLLECTION_RESTRICT_VIEWPORT))) { + if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) || + ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT))) { return OBJECT_LRT_EXCLUDE; } if (ob->lineart.usage == OBJECT_LRT_INHERIT) { diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 6482d9a9d3e..bf0ab3dc533 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -223,7 +223,7 @@ uint GPU_framebuffer_stack_level_get(void); */ GPUOffScreen *GPU_offscreen_create( - int width, int height, bool depth, bool high_bitdepth, char err_out[256]); + int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]); void GPU_offscreen_free(GPUOffScreen *ofs); void GPU_offscreen_bind(GPUOffScreen *ofs, bool save); void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index e073263f352..edf16f04349 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -131,15 +131,11 @@ void GPU_matrix_project_2fv(const float world[3], float r_win[2]); bool GPU_matrix_unproject_3fv(const float win[3], - const float model[4][4], + const float model_inverted[4][4], const float proj[4][4], const int view[4], float r_world[3]); -void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); - /* 2D Projection Matrix */ void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index f980c8fdcd7..9a1885160b6 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -187,25 +187,30 @@ unsigned int GPU_texture_memory_usage_get(void); * \a mips is the number of mip level to allocate. It must be >= 1. */ GPUTexture *GPU_texture_create_1d( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_1d_array( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_2d( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data); -GPUTexture *GPU_texture_create_2d_array( - const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data); +GPUTexture *GPU_texture_create_2d_array(const char *name, + int w, + int h, + int d, + int mip_len, + eGPUTextureFormat format, + const float *data); GPUTexture *GPU_texture_create_3d(const char *name, int w, int h, int d, - int mips, + int mip_len, eGPUTextureFormat texture_format, eGPUDataFormat data_format, const void *data); GPUTexture *GPU_texture_create_cube( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data); GPUTexture *GPU_texture_create_cube_array( - const char *name, int w, int d, int mips, eGPUTextureFormat format, const float *data); + const char *name, int w, int d, int mip_len, eGPUTextureFormat format, const float *data); /* Special textures. */ GPUTexture *GPU_texture_create_from_vertbuf(const char *name, struct GPUVertBuf *vert); diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index a6f7d43e563..9099a6e4245 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -591,7 +591,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) } GPUOffScreen *GPU_offscreen_create( - int width, int height, bool depth, bool high_bitdepth, char err_out[256]) + int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]) { GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); @@ -600,8 +600,7 @@ GPUOffScreen *GPU_offscreen_create( height = max_ii(1, height); width = max_ii(1, width); - ofs->color = GPU_texture_create_2d( - "ofs_color", width, height, 1, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, nullptr); + ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, nullptr); if (depth) { ofs->depth = GPU_texture_create_2d( diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 062741a6270..cdd56a117db 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -313,7 +313,7 @@ void immAttr1f(uint attr_id, float x) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; } @@ -329,7 +329,7 @@ void immAttr2f(uint attr_id, float x, float y) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -346,7 +346,7 @@ void immAttr3f(uint attr_id, float x, float y, float z) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -364,7 +364,7 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w) setAttrValueBit(attr_id); float *data = (float *)(imm->vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); data[0] = x; data[1] = y; @@ -445,7 +445,7 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) setAttrValueBit(attr_id); uchar *data = imm->vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); data[0] = r; data[1] = g; @@ -463,7 +463,7 @@ void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a) setAttrValueBit(attr_id); uchar *data = imm->vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ + // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); data[0] = r; data[1] = g; diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 37089785e0e..56e72fbeca9 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -758,6 +758,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, /* Only free after GPU_pass_shader_get where GPUUniformBuf * read data from the local tree. */ ntreeFreeLocalTree(localtree); + BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(localtree); /* note that even if building the shader fails in some way, we still keep diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 6eb9cb823d5..bbcc241f5e3 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -513,93 +513,55 @@ void GPU_matrix_project_2fv(const float world[3], win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; } -/** - * The same result could be obtained as follows: - * - * \code{.c} - * float projinv[4][4]; - * invert_m4_m4(projinv, projmat); - * co[0] = 2 * co[0] - 1; - * co[1] = 2 * co[1] - 1; - * co[2] = 2 * co[2] - 1; - * mul_project_m4_v3(projinv, co); - * \endcode - * - * But that solution loses much precision. - * Therefore, get the same result without inverting the matrix. - */ -static void gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc( - const struct GPUMatrixUnproject_Precalc *precalc, float co[3]) -{ - /* 'precalc->dims' is the result of 'projmat_dimensions(proj, ...)'. */ - co[0] = precalc->dims.xmin + co[0] * (precalc->dims.xmax - precalc->dims.xmin); - co[1] = precalc->dims.ymin + co[1] * (precalc->dims.ymax - precalc->dims.ymin); - - if (precalc->is_persp) { - co[2] = precalc->dims.zmax * precalc->dims.zmin / - (precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax)); - co[0] *= co[2]; - co[1] *= co[2]; - } - else { - co[2] = precalc->dims.zmin + co[2] * (precalc->dims.zmax - precalc->dims.zmin); - } - co[2] *= -1; -} - -bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, - const float model[4][4], - const float proj[4][4], - const int view[4]) -{ - precalc->is_persp = proj[3][3] == 0.0f; - projmat_dimensions_db(proj, - &precalc->dims.xmin, - &precalc->dims.xmax, - &precalc->dims.ymin, - &precalc->dims.ymax, - &precalc->dims.zmin, - &precalc->dims.zmax); - if (isinf(precalc->dims.zmax)) { - /* We cannot retrieve the actual value of the clip_end. - * Use `FLT_MAX` to avoid NAN's. */ - precalc->dims.zmax = FLT_MAX; - } - for (int i = 0; i < 4; i++) { - precalc->view[i] = (float)view[i]; - } - if (!invert_m4_m4(precalc->model_inverted, model)) { - unit_m4(precalc->model_inverted); - return false; - } - return true; -} - -void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) -{ - float in[3] = { - (win[0] - precalc->view[0]) / precalc->view[2], - (win[1] - precalc->view[1]) / precalc->view[3], - win[2], - }; - gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(precalc, in); - mul_v3_m4v3(r_world, precalc->model_inverted, in); -} - bool GPU_matrix_unproject_3fv(const float win[3], - const float model[4][4], + const float model_inverted[4][4], const float proj[4][4], const int view[4], float r_world[3]) { - struct GPUMatrixUnproject_Precalc precalc; - if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { - zero_v3(r_world); + zero_v3(r_world); + float in[3] = { + 2 * ((win[0] - view[0]) / view[2]) - 1.0f, + 2 * ((win[1] - view[1]) / view[3]) - 1.0f, + 2 * win[2] - 1.0f, + }; + + /** + * The same result could be obtained as follows: + * + * \code{.c} + * float projinv[4][4]; + * invert_m4_m4(projinv, projview); + * copy_v3_v3(r_world, in); + * mul_project_m4_v3(projinv, r_world); + * \endcode + * + * But that solution loses much precision. + * Therefore, get the same result without inverting the project view matrix. + */ + + float out[3]; + const bool is_persp = proj[3][3] == 0.0f; + if (is_persp) { + out[2] = proj[3][2] / (proj[2][2] + in[2]); + if (isinf(out[2])) { + out[2] = FLT_MAX; + } + out[0] = out[2] * ((proj[2][0] + in[0]) / proj[0][0]); + out[1] = out[2] * ((proj[2][1] + in[1]) / proj[1][1]); + out[2] *= -1; + } + else { + out[0] = (-proj[3][0] + in[0]) / proj[0][0]; + out[1] = (-proj[3][1] + in[1]) / proj[1][1]; + out[2] = (-proj[3][2] + in[2]) / proj[2][2]; + } + + if (!is_finite_v3(out)) { return false; } - GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); + + mul_v3_m4v3(r_world, model_inverted, out); return true; } diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 6564cbda694..d5d13ea269f 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -241,55 +241,61 @@ static inline GPUTexture *gpu_texture_create(const char *name, } GPUTexture *GPU_texture_create_1d( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data) + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data) { - return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mip_len, format, GPU_DATA_FLOAT, data); } GPUTexture *GPU_texture_create_1d_array( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data) + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data) { return gpu_texture_create( - name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mip_len, format, GPU_DATA_FLOAT, data); } GPUTexture *GPU_texture_create_2d( - const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data) + const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data) { - return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mip_len, format, GPU_DATA_FLOAT, data); } -GPUTexture *GPU_texture_create_2d_array( - const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data) +GPUTexture *GPU_texture_create_2d_array(const char *name, + int w, + int h, + int d, + int mip_len, + eGPUTextureFormat format, + const float *data) { return gpu_texture_create( - name, w, h, d, GPU_TEXTURE_2D_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, h, d, GPU_TEXTURE_2D_ARRAY, mip_len, format, GPU_DATA_FLOAT, data); } GPUTexture *GPU_texture_create_3d(const char *name, int w, int h, int d, - int mips, + int mip_len, eGPUTextureFormat texture_format, eGPUDataFormat data_format, const void *data) { return gpu_texture_create( - name, w, h, d, GPU_TEXTURE_3D, mips, texture_format, data_format, data); + name, w, h, d, GPU_TEXTURE_3D, mip_len, texture_format, data_format, data); } GPUTexture *GPU_texture_create_cube( - const char *name, int w, int mips, eGPUTextureFormat format, const float *data) + const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data) { - return gpu_texture_create(name, w, w, 0, GPU_TEXTURE_CUBE, mips, format, GPU_DATA_FLOAT, data); + return gpu_texture_create( + name, w, w, 0, GPU_TEXTURE_CUBE, mip_len, format, GPU_DATA_FLOAT, data); } GPUTexture *GPU_texture_create_cube_array( - const char *name, int w, int d, int mips, eGPUTextureFormat format, const float *data) + const char *name, int w, int d, int mip_len, eGPUTextureFormat format, const float *data) { return gpu_texture_create( - name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mips, format, GPU_DATA_FLOAT, data); + name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mip_len, format, GPU_DATA_FLOAT, data); } /* DDS texture loading. Return NULL if support is not available. */ diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 42b85da1f93..772fc19d919 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -283,7 +283,8 @@ static void detect_workarounds() } /* We have issues with this specific renderer. (see T74024) */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && - strstr(renderer, "AMD VERDE")) { + (strstr(renderer, "AMD VERDE") || strstr(renderer, "AMD KAVERI") || + strstr(renderer, "AMD TAHITI"))) { GLContext::unused_fb_slot_workaround = true; GCaps.shader_image_load_store_support = false; GCaps.broken_amd_driver = true; diff --git a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl index f5b6d2ea3ed..5d67658c639 100644 --- a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl @@ -14,14 +14,14 @@ void main() gl_Position = ModelViewProjectionMatrix * pos_4d; gl_PointSize = size; - // calculate concentric radii in pixels + /* Calculate concentric radii in pixels. */ float radius = 0.5 * size; - // start at the outside and progress toward the center + /* Start at the outside and progress toward the center. */ radii[0] = radius; radii[1] = radius - 1.0; - // convert to PointCoord units + /* Convert to PointCoord units. */ radii /= size; #ifdef USE_WORLD_CLIP_PLANES diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc index 43ff86ebbd8..dbe336af097 100644 --- a/source/blender/gpu/tests/gpu_shader_test.cc +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -108,7 +108,8 @@ void main() { EXPECT_NE(shader, nullptr); /* Construct Texture. */ - GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL); + GPUTexture *texture = GPU_texture_create_1d( + "gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr); EXPECT_NE(texture, nullptr); GPU_shader_bind(shader); diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d527aca184c..4ad7aa98484 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -367,6 +367,11 @@ void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc); /** + * Return the encoded start offset (in seconds) of the given \a anim. + */ +double IMD_anim_get_offset(struct anim *anim); + +/** * Return the fps contained in movie files (function rval is false, * and frs_sec and frs_sec_base untouched if none available!) */ diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index cfeffcca0ea..c4e2ad9da7f 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -91,6 +91,7 @@ struct anim { int duration_in_frames; int frs_sec; double frs_sec_base; + double start_offset; int x, y; /* for number */ diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 47514308ae4..dbca16ca82b 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -425,6 +425,7 @@ static int startavi(struct anim *anim) } anim->duration_in_frames = anim->avi->header->TotalFrames; + anim->start_offset = 0.0f; anim->params = NULL; anim->x = anim->avi->header->Width; @@ -597,6 +598,13 @@ static int startffmpeg(struct anim *anim) return -1; } + double video_start = 0; + double pts_time_base = av_q2d(video_stream->time_base); + + if (video_stream->start_time != AV_NOPTS_VALUE) { + video_start = video_stream->start_time * pts_time_base; + } + frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, NULL); anim->duration_in_frames = 0; @@ -616,10 +624,49 @@ static int startffmpeg(struct anim *anim) } } } - /* Fall back to the container. */ + /* Fall back to manually estimating the video stream duration. + * This is because the video stream duration can be shorter than the pFormatCtx->duration. + */ if (anim->duration_in_frames == 0) { - anim->duration_in_frames = (int)(pFormatCtx->duration * av_q2d(frame_rate) / AV_TIME_BASE + - 0.5f); + double stream_dur; + + if (video_stream->duration != AV_NOPTS_VALUE) { + stream_dur = video_stream->duration * pts_time_base; + } + else { + double audio_start = 0; + + /* Find audio stream to guess the duration of the video. + * Sometimes the audio AND the video stream have a start offset. + * The difference between these is the offset we want to use to + * calculate the video duration. + */ + for (i = 0; i < pFormatCtx->nb_streams; i++) { + if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + AVStream *audio_stream = pFormatCtx->streams[i]; + if (audio_stream->start_time != AV_NOPTS_VALUE) { + audio_start = audio_stream->start_time * av_q2d(audio_stream->time_base); + } + break; + } + } + + if (video_start > audio_start) { + stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE - (video_start - audio_start); + } + else { + /* The video stream starts before or at the same time as the audio stream! + * We have to assume that the video stream is as long as the full pFormatCtx->duration. + */ + stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE; + } + } + anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); + } + + double ctx_start = 0; + if (pFormatCtx->start_time != AV_NOPTS_VALUE) { + ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE; } frs_num = frame_rate.num; @@ -634,6 +681,9 @@ static int startffmpeg(struct anim *anim) anim->frs_sec = frs_num; anim->frs_sec_base = frs_den; + /* Save the relative start time for the video. IE the start time in relation to where playback + * starts. */ + anim->start_offset = video_start - ctx_start; anim->params = 0; @@ -1019,33 +1069,21 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } -static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) +static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t st_time = anim->pFormatCtx->start_time; - int64_t pos = (int64_t)(position)*AV_TIME_BASE; - /* Step back half a time base position to make sure that we get the requested - * frame and not the one after it. + AVRational frame_rate = v_st->r_frame_rate; + AVRational time_base = v_st->time_base; + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); + /* Step back half a frame position to make sure that we get the requested + * frame and not the one after it. This is a workaround as ffmpeg will + * sometimes not seek to a frame after the requested pts even if + * AVSEEK_FLAG_BACKWARD is specified. */ - pos -= (AV_TIME_BASE / 2); - pos /= frame_rate; - - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", - pos, - (st_time != AV_NOPTS_VALUE) ? st_time : 0); - - if (pos < 0) { - pos = 0; - } - - if (st_time != AV_NOPTS_VALUE) { - pos += st_time; - } + int64_t pts = pts_to_search - (steps_per_frame / 2); - return pos; + return pts; } /* This gives us an estimate of which pts our requested frame will have. @@ -1062,17 +1100,18 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index); } else { - int64_t st_time = anim->pFormatCtx->start_time; AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL); + int64_t start_pts = v_st->start_time; + AVRational frame_rate = v_st->r_frame_rate; AVRational time_base = v_st->time_base; - int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num); - pts_to_search = position * steps_per_frame; + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); - if (st_time != AV_NOPTS_VALUE && st_time != 0) { - int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate); - pts_to_search += start_frame * steps_per_frame; + pts_to_search = round(position * steps_per_frame); + + if (start_pts != AV_NOPTS_VALUE) { + pts_to_search += start_pts; } } return pts_to_search; @@ -1156,23 +1195,29 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and * https://developer.blender.org/T86944. */ static int ffmpeg_generic_seek_workaround(struct anim *anim, - int64_t *requested_pos, + int64_t *requested_pts, int64_t pts_to_search) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t current_pos = *requested_pos; + AVRational frame_rate = v_st->r_frame_rate; + AVRational time_base = v_st->time_base; + + double steps_per_frame = (double)(frame_rate.den * time_base.den) / + (double)(frame_rate.num * time_base.num); + + int64_t current_pts = *requested_pts; int64_t offset = 0; int64_t cur_pts, prev_pts = -1; /* Step backward frame by frame until we find the key frame we are looking for. */ - while (current_pos != 0) { - current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); - current_pos = max_ii(current_pos, 0); + while (current_pts != 0) { + current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame); + current_pts = MAX2(current_pts, 0); /* Seek to timestamp. */ - if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) { + if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) < + 0) { break; } @@ -1198,21 +1243,22 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, /* We found the I-frame we were looking for! */ break; } - if (cur_pts == prev_pts) { - /* We got the same key frame packet twice. - * This probably means that we have hit the beginning of the stream. */ - break; - } + } + + if (cur_pts == prev_pts) { + /* We got the same key frame packet twice. + * This probably means that we have hit the beginning of the stream. */ + break; } prev_pts = cur_pts; offset++; } - *requested_pos = current_pos; + *requested_pts = current_pts; /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */ - return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD); + return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD); } /* Seek to last necessary key frame. */ @@ -1260,13 +1306,13 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, else { /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. */ - pos = ffmpeg_get_seek_pos(anim, position); + pos = ffmpeg_get_seek_pts(anim, pts_to_search); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); AVFormatContext *format_ctx = anim->pFormatCtx; if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) { - ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); } else { ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search); @@ -1311,7 +1357,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, anim->cur_key_frame_pts = gop_pts; /* Seek back so we are at the correct position after we decoded a frame. */ - av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); + av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); } } @@ -1351,18 +1397,18 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ struct anim_index *tc_index = IMB_anim_open_index(anim, tc); int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); + double frame_rate = av_q2d(v_st->r_frame_rate); double pts_time_base = av_q2d(v_st->time_base); - int64_t st_time = anim->pFormatCtx->start_time; + int64_t start_pts = v_st->start_time; av_log(anim->pFormatCtx, AV_LOG_DEBUG, - "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64 + "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64 ")\n", (int64_t)pts_to_search, pts_time_base, frame_rate, - st_time); + start_pts); if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) { av_log(anim->pFormatCtx, @@ -1637,6 +1683,11 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc) return IMB_indexer_get_duration(idx); } +double IMD_anim_get_offset(struct anim *anim) +{ + return anim->start_offset; +} + bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bool no_av_base) { double frs_sec_base_double; diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 2cc44ebc67b..8b2ac2ed22d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -759,11 +759,7 @@ static bool colormanage_use_look(const char *look, const char *view_name) void colormanage_cache_free(ImBuf *ibuf) { - if (ibuf->display_buffer_flags) { - MEM_freeN(ibuf->display_buffer_flags); - - ibuf->display_buffer_flags = NULL; - } + MEM_SAFE_FREE(ibuf->display_buffer_flags); if (ibuf->colormanage_cache) { ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf); diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h index 158695cfbf3..934837bb129 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.h +++ b/source/blender/imbuf/intern/dds/ColorBlock.h @@ -25,7 +25,7 @@ * Original license from NVIDIA follows. */ -// This code is in the public domain -- <castanyo@yahoo.es> +/* This code is in the public domain -- <castanyo@yahoo.es> */ #pragma once diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 27195b294d6..bbb0f3b5b22 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1022,7 +1022,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, stream_size = avio_size(context->iFormatCtx->pb); - context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL)); + context->frame_rate = av_q2d(context->iStream->r_frame_rate); context->pts_time_base = av_q2d(context->iStream->time_base); while (av_read_frame(context->iFormatCtx, next_packet) >= 0) { diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c index c59997b34f5..5a01c42cf00 100644 --- a/source/blender/imbuf/intern/metadata.c +++ b/source/blender/imbuf/intern/metadata.c @@ -44,7 +44,7 @@ void IMB_metadata_ensure(struct IDProperty **metadata) return; } - IDPropertyTemplate val; + IDPropertyTemplate val = {0}; *metadata = IDP_New(IDP_GROUP, &val, "metadata"); } diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index e395222a214..6cc1932eff6 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -179,10 +179,7 @@ static void IMB_moviecache_destructor(void *p) item->c_handle = NULL; /* force cached segments to be updated */ - if (cache->points) { - MEM_freeN(cache->points); - cache->points = NULL; - } + MEM_SAFE_FREE(cache->points); } } @@ -355,10 +352,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo /* cache limiter can't remove unused keys which points to destroyed values */ check_unused_keys(cache); - if (cache->points) { - MEM_freeN(cache->points); - cache->points = NULL; - } + MEM_SAFE_FREE(cache->points); } void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) @@ -488,11 +482,7 @@ void IMB_moviecache_get_cache_segments( } if (cache->proxy != proxy || cache->render_flags != render_flags) { - if (cache->points) { - MEM_freeN(cache->points); - } - - cache->points = NULL; + MEM_SAFE_FREE(cache->points); } if (cache->points) { diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index d1fa26e1a3e..cd323e72003 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -69,7 +69,7 @@ extern "C" { -// The following prevents a linking error in debug mode for MSVC using the libs in CVS +/* The following prevents a linking error in debug mode for MSVC using the libs in SVN. */ #if defined(WITH_OPENEXR) && defined(_WIN32) && defined(DEBUG) && _MSC_VER < 1900 _CRTIMP void __cdecl _invalid_parameter_noinfo(void) { @@ -1711,7 +1711,7 @@ static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *ch const ChannelList &channels = file.header(0).channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { - /* const Channel &channel = i.channel(); */ /* Not used yet */ + // const Channel &channel = i.channel(); /* Not used yet. */ const char *str = i.name(); int len = strlen(str); if (len) { diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 3d1391ac2a4..0a3a43bb21f 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -25,6 +25,7 @@ extern "C" { #endif +struct CacheArchiveHandle; struct CacheReader; struct ListBase; struct Main; @@ -33,8 +34,6 @@ struct Object; struct Scene; struct bContext; -typedef struct AbcArchiveHandle AbcArchiveHandle; - int ABC_get_version(void); struct AlembicExportParams { @@ -98,13 +97,14 @@ bool ABC_import(struct bContext *C, int sequence_len, int offset, bool validate_meshes, + bool always_add_cache_reader, bool as_background_job); -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - struct ListBase *object_paths); +struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + struct ListBase *object_paths); -void ABC_free_handle(AbcArchiveHandle *handle); +void ABC_free_handle(struct CacheArchiveHandle *handle); void ABC_get_transform(struct CacheReader *reader, float r_mat_world[4][4], @@ -125,10 +125,10 @@ bool ABC_mesh_topology_changed(struct CacheReader *reader, const float time, const char **err_str); -void CacheReader_incref(struct CacheReader *reader); -void CacheReader_free(struct CacheReader *reader); +void ABC_CacheReader_incref(struct CacheReader *reader); +void ABC_CacheReader_free(struct CacheReader *reader); -struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle, +struct CacheReader *CacheReader_open_alembic_object(struct CacheArchiveHandle *handle, struct CacheReader *reader, struct Object *object, const char *object_path); diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 8d6605d6973..27ee35d1b39 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -112,7 +112,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele read_curve_sample(cu, m_curves_schema, sample_sel); - if (has_animations(m_curves_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_curves_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index c05df7f1ff5..77edd4908bd 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -578,7 +578,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec readFaceSetsSample(bmain, mesh, sample_sel); - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } @@ -928,7 +928,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec BKE_mesh_validate(mesh, false, false); } - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index 2a5f4ecb787..25567aa8c24 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -90,7 +90,7 @@ static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF)); + Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, m_data_name.c_str(), OB_SURF)); cu->actvert = CU_ACT_NONE; std::vector<std::pair<INuPatchSchema, IObject>>::iterator it; @@ -180,8 +180,6 @@ void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele BLI_addtail(BKE_curve_nurbs_get(cu), nu); } - BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); - m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); m_object->data = cu; } diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index 00b73d29c5c..9a5ffd04bd1 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -197,7 +197,7 @@ void AbcObjectReader::setupObjectTransform(const float time) BKE_object_apply_mat4(m_object, transform_from_alembic, true, false); BKE_object_to_mat4(m_object, m_object->obmat); - if (!is_constant) { + if (!is_constant || m_settings->always_add_cache_reader) { bConstraint *con = BKE_constraint_add_for_object( m_object, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE); bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index dacdcf3f722..89590b26b61 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -51,6 +51,7 @@ struct ImportSettings { int read_flag; bool validate_meshes; + bool always_add_cache_reader; CacheFile *cache_file; @@ -65,6 +66,7 @@ struct ImportSettings { sequence_offset(0), read_flag(0), validate_meshes(false), + always_add_cache_reader(false), cache_file(NULL) { } diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index f7dcba7a0de..3aeacbd14fe 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -95,7 +95,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - if (has_animations(m_schema, m_settings)) { + if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h index 98f4b0376a7..ced9fde0f85 100644 --- a/source/blender/io/alembic/intern/abc_util.h +++ b/source/blender/io/alembic/intern/abc_util.h @@ -22,15 +22,6 @@ #include <Alembic/Abc/All.h> #include <Alembic/AbcGeom/All.h> -/** - * \brief The CacheReader struct is only used for anonymous pointers, - * to interface between C and C++ code. This library only creates - * pointers to AbcObjectReader (or subclasses thereof). - */ -struct CacheReader { - int unused; -}; - using Alembic::Abc::chrono_t; struct ID; diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index e8d70bf3edb..deb945b767c 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -19,6 +19,7 @@ */ #include "../ABC_alembic.h" +#include "IO_types.h" #include <Alembic/AbcMaterial/IMaterial.h> @@ -89,18 +90,14 @@ using Alembic::AbcMaterial::IMaterial; using namespace blender::io::alembic; -struct AbcArchiveHandle { - int unused; -}; - -BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) +BLI_INLINE ArchiveReader *archive_from_handle(CacheArchiveHandle *handle) { return reinterpret_cast<ArchiveReader *>(handle); } -BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) +BLI_INLINE CacheArchiveHandle *handle_from_archive(ArchiveReader *archive) { - return reinterpret_cast<AbcArchiveHandle *>(archive); + return reinterpret_cast<CacheArchiveHandle *>(archive); } //#define USE_NURBS @@ -150,8 +147,8 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths) } if (get_path) { - void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"); - AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(abc_path_void); + void *abc_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath"); + CacheObjectPath *abc_path = static_cast<CacheObjectPath *>(abc_path_void); BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path)); BLI_addtail(object_paths, abc_path); @@ -160,9 +157,9 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths) return parent_is_part_of_this_object; } -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - ListBase *object_paths) +CacheArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + ListBase *object_paths) { ArchiveReader *archive = new ArchiveReader(bmain, filename); @@ -178,7 +175,7 @@ AbcArchiveHandle *ABC_create_handle(struct Main *bmain, return handle_from_archive(archive); } -void ABC_free_handle(AbcArchiveHandle *handle) +void ABC_free_handle(CacheArchiveHandle *handle) { delete archive_from_handle(handle); } @@ -359,8 +356,8 @@ static std::pair<bool, AbcObjectReader *> visit_object( readers.push_back(reader); reader->incref(); - AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( - MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + CacheObjectPath *abc_path = static_cast<CacheObjectPath *>( + MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath")); BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path)); BLI_addtail(&settings.cache_file->object_paths, abc_path); @@ -666,6 +663,7 @@ bool ABC_import(bContext *C, int sequence_len, int offset, bool validate_meshes, + bool always_add_cache_reader, bool as_background_job) { /* Using new here since MEM_* functions do not call constructor to properly initialize data. */ @@ -684,6 +682,7 @@ bool ABC_import(bContext *C, job->settings.sequence_len = sequence_len; job->settings.sequence_offset = offset; job->settings.validate_meshes = validate_meshes; + job->settings.always_add_cache_reader = always_add_cache_reader; job->error_code = ABC_NO_ERROR; job->was_cancelled = false; job->archive = nullptr; @@ -812,7 +811,7 @@ bool ABC_mesh_topology_changed( /* ************************************************************************** */ -void CacheReader_free(CacheReader *reader) +void ABC_CacheReader_free(CacheReader *reader) { AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); abc_reader->decref(); @@ -822,13 +821,13 @@ void CacheReader_free(CacheReader *reader) } } -void CacheReader_incref(CacheReader *reader) +void ABC_CacheReader_incref(CacheReader *reader) { AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); abc_reader->incref(); } -CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, +CacheReader *CacheReader_open_alembic_object(CacheArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path) @@ -847,7 +846,7 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, find_iobject(archive->getTop(), iobject, object_path); if (reader) { - CacheReader_free(reader); + ABC_CacheReader_free(reader); } ImportSettings settings; diff --git a/source/blender/io/avi/intern/avi_endian.c b/source/blender/io/avi/intern/avi_endian.c index 146206cd917..36cee7bcadc 100644 --- a/source/blender/io/avi/intern/avi_endian.c +++ b/source/blender/io/avi/intern/avi_endian.c @@ -42,7 +42,7 @@ static void invert(int *val) { int tval = *val; - *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); + *val = (tval >> 24) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | (tval << 24); } static void sinvert(short int *val) diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index e52bdca0d87..e54192abc54 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -1363,7 +1363,7 @@ void AnimationImporter::add_bone_animation_sampled(Object *ob, calc_joint_parent_mat_rest(par, nullptr, root, node); mul_m4_m4m4(temp, par, matfra); - /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ + // evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); /* calc special matrix */ mul_m4_series(mat, irest, temp, irest_dae, rest); diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp index 5bbd22b8275..09da2288cfc 100644 --- a/source/blender/io/collada/SceneExporter.cpp +++ b/source/blender/io/collada/SceneExporter.cpp @@ -172,7 +172,7 @@ void SceneExporter::writeNode(Object *ob) else if (ob->type == OB_EMPTY) { /* TODO: handle groups (OB_DUPLICOLLECTION */ if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->instance_collection) { Collection *collection = ob->instance_collection; - /* printf("group detected '%s'\n", group->id.name + 2); */ + // printf("group detected '%s'\n", group->id.name + 2); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) { printf("\t%s\n", object->id.name); } diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp index 355aa5c22f0..bd6f496c8ec 100644 --- a/source/blender/io/collada/collada_internal.cpp +++ b/source/blender/io/collada/collada_internal.cpp @@ -162,7 +162,7 @@ void UnitConverter::calculate_scale(Scene &sce) * Translation map. * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be * included. Look at the IDREF XSD declaration for more. - * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, + * Follows strictly the COLLADA XSD declaration which explicitly allows non-English chars, * like special chars (e.g. micro sign), umlauts and so on. * The COLLADA spec also allows additional chars for member access ('.'), these * must obviously be removed too, otherwise they would be heavily misinterpreted. diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 7e39af32f11..2aaf5d57fd6 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -37,6 +37,7 @@ set(SRC IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_types.h intern/dupli_parent_finder.hh ) diff --git a/source/blender/io/common/IO_types.h b/source/blender/io/common/IO_types.h new file mode 100644 index 00000000000..4570e29f6ed --- /dev/null +++ b/source/blender/io/common/IO_types.h @@ -0,0 +1,34 @@ +/* + * 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. + */ +#pragma once + +/* The CacheArchiveHandle struct is only used for anonymous pointers, + * to interface between C and C++ code. This is currently used + * to hide pointers to alembic ArchiveReader and USDStageReader. */ +struct CacheArchiveHandle { + int unused; +}; + +/* The CacheReader struct is only used for anonymous pointers, + * to interface between C and C++ code. This is currently used + * to hide pointers to AbcObjectReader and USDPrimReader + * (or subclasses thereof). */ +struct CacheReader { + int unused; +}; diff --git a/source/blender/io/gpencil/CMakeLists.txt b/source/blender/io/gpencil/CMakeLists.txt index fec95be6aa8..4af8b506bd5 100644 --- a/source/blender/io/gpencil/CMakeLists.txt +++ b/source/blender/io/gpencil/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/utfconv + ../../../../extern/nanosvg ) set(INC_SYS @@ -44,9 +45,6 @@ set(SRC intern/gpencil_io_import_base.cc intern/gpencil_io_import_svg.cc - # This line must be removed if NanoSVG is moved to extern - nanosvg/nanosvg.h - gpencil_io.h intern/gpencil_io_base.hh intern/gpencil_io_export_base.hh diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc index db6bbc7768e..941d1137f4d 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc @@ -42,7 +42,7 @@ #define NANOSVG_ALL_COLOR_KEYWORDS #define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" +#include "nanosvg.h" using blender::MutableSpan; diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h deleted file mode 100644 index 94dad37861a..00000000000 --- a/source/blender/io/gpencil/nanosvg/nanosvg.h +++ /dev/null @@ -1,3327 +0,0 @@ -/* - * Copyright (c) 2013-14 `Mikko Mononen <memon@inside.org>` - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example - * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) - * - * Arc calculation code based on canvg (https://code.google.com/p/canvg/) - * - * Bounding box calculation based on - * http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - * - * This is a modified version for Blender used by importers. - * - */ - -#ifndef NANOSVG_H -#define NANOSVG_H - -#ifndef NANOSVG_CPLUSPLUS -# ifdef __cplusplus -extern "C" { -# endif -#endif - -// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of -// cubic bezier shapes. -// -// The library suits well for anything from rendering scalable icons in your editor application to -// prototyping a game. -// -// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create -// a pull request! -// -// The shapes in the SVG images are transformed by the viewBox and converted to specified units. -// That is, you should get the same looking data as your designed in your favorite app. -// -// NanoSVG can return the paths in few different units. For example if you want to render an image, -// you may choose to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you -// may want to use millimeters. -// -// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. -// DPI (dots-per-inch) controls how the unit conversion is done. -// -// If you don't know or care about the units stuff, "px" and 96 should get you going. - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - printf("size: %f x %f\n", image->width, image->height); - // Use... - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); - } - } - } - // Delete - nsvgDelete(image); -*/ - -enum NSVGpaintType { - NSVG_PAINT_NONE = 0, - NSVG_PAINT_COLOR = 1, - NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3 -}; - -enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REPEAT = 2 }; - -enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, NSVG_JOIN_BEVEL = 2 }; - -enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, NSVG_CAP_SQUARE = 2 }; - -enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_EVENODD = 1 }; - -enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 }; - -typedef struct NSVGgradientStop { - unsigned int color; - float offset; -} NSVGgradientStop; - -typedef struct NSVGgradient { - float xform[6]; - char spread; - float fx, fy; - int nstops; - NSVGgradientStop stops[1]; -} NSVGgradient; - -typedef struct NSVGpaint { - char type; - union { - unsigned int color; - NSVGgradient *gradient; - }; -} NSVGpaint; - -typedef struct NSVGpath { - float *pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... - int npts; // Total number of bezier points. - char closed; // Flag indicating if shapes should be treated as closed. - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - struct NSVGpath *next; // Pointer to next path, or NULL if last element. -} NSVGpath; - -typedef struct NSVGshape { - char id[64]; // Optional 'id' attr of the shape or its group - /* Blender: Parent ID used for layer creation. */ - char id_parent[64]; - NSVGpaint fill; // Fill paint - NSVGpaint stroke; // Stroke paint - float opacity; // Opacity of the shape. - float strokeWidth; // Stroke width (scaled). - float strokeDashOffset; // Stroke dash offset (scaled). - float strokeDashArray[8]; // Stroke dash array (scaled). - char strokeDashCount; // Number of dash values in dash array. - char strokeLineJoin; // Stroke join type. - char strokeLineCap; // Stroke cap type. - float miterLimit; // Miter limit - char fillRule; // Fill rule, see NSVGfillRule. - unsigned char flags; // Logical or of NSVG_FLAGS_* flags - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - NSVGpath *paths; // Linked list of paths in the image. - struct NSVGshape *next; // Pointer to next shape, or NULL if last element. -} NSVGshape; - -typedef struct NSVGimage { - float width; // Width of the image. - float height; // Height of the image. - NSVGshape *shapes; // Linked list of shapes in the image. -} NSVGimage; - -// Parses SVG file from a file, returns SVG image as paths. -NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi); - -// Parses SVG file from a null terminated string, returns SVG image as paths. -// Important note: changes the string. -NSVGimage *nsvgParse(char *input, const char *units, float dpi); - -// Duplicates a path. -NSVGpath *nsvgDuplicatePath(NSVGpath *p); - -// Deletes an image. -void nsvgDelete(NSVGimage *image); - -#ifndef NANOSVG_CPLUSPLUS -# ifdef __cplusplus -} -# endif -#endif - -#endif // NANOSVG_H - -#ifdef NANOSVG_IMPLEMENTATION - -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 \ - (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NSVG_ALIGN_MIN 0 -#define NSVG_ALIGN_MID 1 -#define NSVG_ALIGN_MAX 2 -#define NSVG_ALIGN_NONE 0 -#define NSVG_ALIGN_MEET 1 -#define NSVG_ALIGN_SLICE 2 - -#define NSVG_NOTUSED(v) \ - do { \ - (void)(1 ? (void)0 : ((void)(v))); \ - } while (0) -#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) - -#ifdef _MSC_VER -# pragma warning(disable : 4996) // Switch off security warnings -# pragma warning(disable : 4100) // Switch off unreferenced formal parameter warnings -# ifdef __cplusplus -# define NSVG_INLINE inline -# else -# define NSVG_INLINE -# endif -#else -# define NSVG_INLINE inline -#endif - -static int nsvg__isspace(char c) -{ - return strchr(" \t\n\v\f\r", c) != 0; -} - -static int nsvg__isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static NSVG_INLINE float nsvg__minf(float a, float b) -{ - return a < b ? a : b; -} -static NSVG_INLINE float nsvg__maxf(float a, float b) -{ - return a > b ? a : b; -} - -// Simple XML parser - -#define NSVG_XML_TAG 1 -#define NSVG_XML_CONTENT 2 -#define NSVG_XML_MAX_ATTRIBS 256 - -static void nsvg__parseContent(char *s, void (*contentCb)(void *ud, const char *s), void *ud) -{ - // Trim start white spaces - while (*s && nsvg__isspace(*s)) - s++; - if (!*s) - return; - - if (contentCb) - (*contentCb)(ud, s); -} - -static void nsvg__parseElement(char *s, - void (*startelCb)(void *ud, const char *el, const char **attr), - void (*endelCb)(void *ud, const char *el), - void *ud) -{ - const char *attr[NSVG_XML_MAX_ATTRIBS]; - int nattr = 0; - char *name; - int start = 0; - int end = 0; - char quote; - - // Skip white space after the '<' - while (*s && nsvg__isspace(*s)) - s++; - - // Check if the tag is end tag - if (*s == '/') { - s++; - end = 1; - } - else { - start = 1; - } - - // Skip comments, data and preprocessor stuff. - if (!*s || *s == '?' || *s == '!') - return; - - // Get tag name - name = s; - while (*s && !nsvg__isspace(*s)) - s++; - if (*s) { - *s++ = '\0'; - } - - // Get attribs - while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS - 3) { - char *name = NULL; - char *value = NULL; - - // Skip white space before the attrib name - while (*s && nsvg__isspace(*s)) - s++; - if (!*s) - break; - if (*s == '/') { - end = 1; - break; - } - name = s; - // Find end of the attrib name. - while (*s && !nsvg__isspace(*s) && *s != '=') - s++; - if (*s) { - *s++ = '\0'; - } - // Skip until the beginning of the value. - while (*s && *s != '\"' && *s != '\'') - s++; - if (!*s) - break; - quote = *s; - s++; - // Store value and find the end of it. - value = s; - while (*s && *s != quote) - s++; - if (*s) { - *s++ = '\0'; - } - - // Store only well formed attributes - if (name && value) { - attr[nattr++] = name; - attr[nattr++] = value; - } - } - - // List terminator - attr[nattr++] = 0; - attr[nattr++] = 0; - - // Call callbacks. - if (start && startelCb) - (*startelCb)(ud, name, attr); - if (end && endelCb) - (*endelCb)(ud, name); -} - -static int nsvg__parseXML(char *input, - void (*startelCb)(void *ud, const char *el, const char **attr), - void (*endelCb)(void *ud, const char *el), - void (*contentCb)(void *ud, const char *s), - void *ud) -{ - char *s = input; - char *mark = s; - int state = NSVG_XML_CONTENT; - while (*s) { - if (*s == '<' && state == NSVG_XML_CONTENT) { - // Start of a tag - *s++ = '\0'; - nsvg__parseContent(mark, contentCb, ud); - mark = s; - state = NSVG_XML_TAG; - } - else if (*s == '>' && state == NSVG_XML_TAG) { - // Start of a content or new tag. - *s++ = '\0'; - nsvg__parseElement(mark, startelCb, endelCb, ud); - mark = s; - state = NSVG_XML_CONTENT; - } - else { - s++; - } - } - - return 1; -} - -/* Simple SVG parser. */ - -#define NSVG_MAX_ATTR 128 -#define NSVG_MAX_BREADCRUMB 5 - -enum NSVGgradientUnits { NSVG_USER_SPACE = 0, NSVG_OBJECT_SPACE = 1 }; - -#define NSVG_MAX_DASHES 8 - -enum NSVGunits { - NSVG_UNITS_USER, - NSVG_UNITS_PX, - NSVG_UNITS_PT, - NSVG_UNITS_PC, - NSVG_UNITS_MM, - NSVG_UNITS_CM, - NSVG_UNITS_IN, - NSVG_UNITS_PERCENT, - NSVG_UNITS_EM, - NSVG_UNITS_EX -}; - -typedef struct NSVGcoordinate { - float value; - int units; -} NSVGcoordinate; - -typedef struct NSVGlinearData { - NSVGcoordinate x1, y1, x2, y2; -} NSVGlinearData; - -typedef struct NSVGradialData { - NSVGcoordinate cx, cy, r, fx, fy; -} NSVGradialData; - -typedef struct NSVGgradientData { - char id[64]; - char ref[64]; - char type; - union { - NSVGlinearData linear; - NSVGradialData radial; - }; - char spread; - char units; - float xform[6]; - int nstops; - NSVGgradientStop *stops; - struct NSVGgradientData *next; -} NSVGgradientData; - -typedef struct NSVGattrib { - char id[64]; - float xform[6]; - unsigned int fillColor; - unsigned int strokeColor; - float opacity; - float fillOpacity; - float strokeOpacity; - char fillGradient[64]; - char strokeGradient[64]; - float strokeWidth; - float strokeDashOffset; - float strokeDashArray[NSVG_MAX_DASHES]; - int strokeDashCount; - char strokeLineJoin; - char strokeLineCap; - float miterLimit; - char fillRule; - float fontSize; - unsigned int stopColor; - float stopOpacity; - float stopOffset; - char hasFill; - char hasStroke; - char visible; -} NSVGattrib; - -typedef struct NSVGparser { - NSVGattrib attr[NSVG_MAX_ATTR]; - int attrHead; - float *pts; - int npts; - int cpts; - NSVGpath *plist; - NSVGimage *image; - NSVGgradientData *gradients; - NSVGshape *shapesTail; - float viewMinx, viewMiny, viewWidth, viewHeight; - int alignX, alignY, alignType; - float dpi; - char pathFlag; - char defsFlag; - /** Blender breadcrumb for layers. */ - char breadcrumb[NSVG_MAX_BREADCRUMB][64]; - /** Blender number of elements in breadcrumb. */ - int breadcrumb_len; -} NSVGparser; - -static void nsvg__xformIdentity(float *t) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetTranslation(float *t, float tx, float ty) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = tx; - t[5] = ty; -} - -static void nsvg__xformSetScale(float *t, float sx, float sy) -{ - t[0] = sx; - t[1] = 0.0f; - t[2] = 0.0f; - t[3] = sy; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetSkewX(float *t, float a) -{ - t[0] = 1.0f; - t[1] = 0.0f; - t[2] = tanf(a); - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetSkewY(float *t, float a) -{ - t[0] = 1.0f; - t[1] = tanf(a); - t[2] = 0.0f; - t[3] = 1.0f; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformSetRotation(float *t, float a) -{ - float cs = cosf(a), sn = sinf(a); - t[0] = cs; - t[1] = sn; - t[2] = -sn; - t[3] = cs; - t[4] = 0.0f; - t[5] = 0.0f; -} - -static void nsvg__xformMultiply(float *t, float *s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -static void nsvg__xformInverse(float *inv, float *t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nsvg__xformIdentity(t); - return; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); -} - -static void nsvg__xformPremultiply(float *t, float *s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float) * 6); - nsvg__xformMultiply(s2, t); - memcpy(t, s2, sizeof(float) * 6); -} - -static void nsvg__xformPoint(float *dx, float *dy, float x, float y, float *t) -{ - *dx = x * t[0] + y * t[2] + t[4]; - *dy = x * t[1] + y * t[3] + t[5]; -} - -static void nsvg__xformVec(float *dx, float *dy, float x, float y, float *t) -{ - *dx = x * t[0] + y * t[2]; - *dy = x * t[1] + y * t[3]; -} - -#define NSVG_EPSILON (1e-12) - -static int nsvg__ptInBounds(float *pt, float *bounds) -{ - return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; -} - -static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) -{ - double it = 1.0 - t; - return it * it * it * p0 + 3.0 * it * it * t * p1 + 3.0 * it * t * t * p2 + t * t * t * p3; -} - -static void nsvg__curveBounds(float *bounds, float *curve) -{ - int i, j, count; - double roots[2], a, b, c, b2ac, t, v; - float *v0 = &curve[0]; - float *v1 = &curve[2]; - float *v2 = &curve[4]; - float *v3 = &curve[6]; - - // Start the bounding box by end points - bounds[0] = nsvg__minf(v0[0], v3[0]); - bounds[1] = nsvg__minf(v0[1], v3[1]); - bounds[2] = nsvg__maxf(v0[0], v3[0]); - bounds[3] = nsvg__maxf(v0[1], v3[1]); - - // Bezier curve fits inside the convex hull of it's control points. - // If control points are inside the bounds, we're done. - if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) - return; - - // Add bezier curve inflection points in X and Y. - for (i = 0; i < 2; i++) { - a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; - b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; - c = 3.0 * v1[i] - 3.0 * v0[i]; - count = 0; - if (fabs(a) < NSVG_EPSILON) { - if (fabs(b) > NSVG_EPSILON) { - t = -c / b; - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - } - } - else { - b2ac = b * b - 4.0 * c * a; - if (b2ac > NSVG_EPSILON) { - t = (-b + sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - t = (-b - sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON) - roots[count++] = t; - } - } - for (j = 0; j < count; j++) { - v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); - bounds[0 + i] = nsvg__minf(bounds[0 + i], (float)v); - bounds[2 + i] = nsvg__maxf(bounds[2 + i], (float)v); - } - } -} - -static NSVGparser *nsvg__createParser() -{ - NSVGparser *p; - p = (NSVGparser *)malloc(sizeof(NSVGparser)); - if (p == NULL) - goto error; - memset(p, 0, sizeof(NSVGparser)); - - p->image = (NSVGimage *)malloc(sizeof(NSVGimage)); - if (p->image == NULL) - goto error; - memset(p->image, 0, sizeof(NSVGimage)); - - // Init style - nsvg__xformIdentity(p->attr[0].xform); - memset(p->attr[0].id, 0, sizeof p->attr[0].id); - p->attr[0].fillColor = NSVG_RGB(0, 0, 0); - p->attr[0].strokeColor = NSVG_RGB(0, 0, 0); - p->attr[0].opacity = 1; - p->attr[0].fillOpacity = 1; - p->attr[0].strokeOpacity = 1; - p->attr[0].stopOpacity = 1; - p->attr[0].strokeWidth = 1; - p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; - p->attr[0].strokeLineCap = NSVG_CAP_BUTT; - p->attr[0].miterLimit = 4; - p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; - p->attr[0].hasFill = 1; - p->attr[0].visible = 1; - - return p; - -error: - if (p) { - if (p->image) - free(p->image); - free(p); - } - return NULL; -} - -static void nsvg__deletePaths(NSVGpath *path) -{ - while (path) { - NSVGpath *next = path->next; - if (path->pts != NULL) - free(path->pts); - free(path); - path = next; - } -} - -static void nsvg__deletePaint(NSVGpaint *paint) -{ - if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) - free(paint->gradient); -} - -static void nsvg__deleteGradientData(NSVGgradientData *grad) -{ - NSVGgradientData *next; - while (grad != NULL) { - next = grad->next; - free(grad->stops); - free(grad); - grad = next; - } -} - -static void nsvg__deleteParser(NSVGparser *p) -{ - if (p != NULL) { - nsvg__deletePaths(p->plist); - nsvg__deleteGradientData(p->gradients); - nsvgDelete(p->image); - free(p->pts); - free(p); - } -} - -static void nsvg__resetPath(NSVGparser *p) -{ - p->npts = 0; -} - -static void nsvg__addPoint(NSVGparser *p, float x, float y) -{ - if (p->npts + 1 > p->cpts) { - p->cpts = p->cpts ? p->cpts * 2 : 8; - p->pts = (float *)realloc(p->pts, p->cpts * 2 * sizeof(float)); - if (!p->pts) - return; - } - p->pts[p->npts * 2 + 0] = x; - p->pts[p->npts * 2 + 1] = y; - p->npts++; -} - -static void nsvg__moveTo(NSVGparser *p, float x, float y) -{ - if (p->npts > 0) { - p->pts[(p->npts - 1) * 2 + 0] = x; - p->pts[(p->npts - 1) * 2 + 1] = y; - } - else { - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__lineTo(NSVGparser *p, float x, float y) -{ - float px, py, dx, dy; - if (p->npts > 0) { - px = p->pts[(p->npts - 1) * 2 + 0]; - py = p->pts[(p->npts - 1) * 2 + 1]; - dx = x - px; - dy = y - py; - nsvg__addPoint(p, px + dx / 3.0f, py + dy / 3.0f); - nsvg__addPoint(p, x - dx / 3.0f, y - dy / 3.0f); - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__cubicBezTo( - NSVGparser *p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) -{ - if (p->npts > 0) { - nsvg__addPoint(p, cpx1, cpy1); - nsvg__addPoint(p, cpx2, cpy2); - nsvg__addPoint(p, x, y); - } -} - -static NSVGattrib *nsvg__getAttr(NSVGparser *p) -{ - return &p->attr[p->attrHead]; -} - -static void nsvg__pushAttr(NSVGparser *p) -{ - if (p->attrHead < NSVG_MAX_ATTR - 1) { - p->attrHead++; - memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead - 1], sizeof(NSVGattrib)); - } -} - -static void nsvg__popAttr(NSVGparser *p) -{ - if (p->attrHead > 0) - p->attrHead--; -} - -static float nsvg__actualOrigX(NSVGparser *p) -{ - return p->viewMinx; -} - -static float nsvg__actualOrigY(NSVGparser *p) -{ - return p->viewMiny; -} - -static float nsvg__actualWidth(NSVGparser *p) -{ - return p->viewWidth; -} - -static float nsvg__actualHeight(NSVGparser *p) -{ - return p->viewHeight; -} - -static float nsvg__actualLength(NSVGparser *p) -{ - float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); - return sqrtf(w * w + h * h) / sqrtf(2.0f); -} - -static float nsvg__convertToPixels(NSVGparser *p, NSVGcoordinate c, float orig, float length) -{ - NSVGattrib *attr = nsvg__getAttr(p); - switch (c.units) { - case NSVG_UNITS_USER: - return c.value; - case NSVG_UNITS_PX: - return c.value; - case NSVG_UNITS_PT: - return c.value / 72.0f * p->dpi; - case NSVG_UNITS_PC: - return c.value / 6.0f * p->dpi; - case NSVG_UNITS_MM: - return c.value / 25.4f * p->dpi; - case NSVG_UNITS_CM: - return c.value / 2.54f * p->dpi; - case NSVG_UNITS_IN: - return c.value * p->dpi; - case NSVG_UNITS_EM: - return c.value * attr->fontSize; - case NSVG_UNITS_EX: - return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. - case NSVG_UNITS_PERCENT: - return orig + c.value / 100.0f * length; - default: - return c.value; - } - return c.value; -} - -static NSVGgradientData *nsvg__findGradientData(NSVGparser *p, const char *id) -{ - NSVGgradientData *grad = p->gradients; - if (id == NULL || *id == '\0') - return NULL; - while (grad != NULL) { - if (strcmp(grad->id, id) == 0) - return grad; - grad = grad->next; - } - return NULL; -} - -static NSVGgradient *nsvg__createGradient(NSVGparser *p, - const char *id, - const float *localBounds, - char *paintType) -{ - NSVGattrib *attr = nsvg__getAttr(p); - NSVGgradientData *data = NULL; - NSVGgradientData *ref = NULL; - NSVGgradientStop *stops = NULL; - NSVGgradient *grad; - float ox, oy, sw, sh, sl; - int nstops = 0; - int refIter; - - data = nsvg__findGradientData(p, id); - if (data == NULL) - return NULL; - - // TODO: use ref to fill in all unset values too. - ref = data; - refIter = 0; - while (ref != NULL) { - NSVGgradientData *nextRef = NULL; - if (stops == NULL && ref->stops != NULL) { - stops = ref->stops; - nstops = ref->nstops; - break; - } - nextRef = nsvg__findGradientData(p, ref->ref); - if (nextRef == ref) - break; // prevent infite loops on malformed data - ref = nextRef; - refIter++; - if (refIter > 32) - break; // prevent infite loops on malformed data - } - if (stops == NULL) - return NULL; - - grad = (NSVGgradient *)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop) * (nstops - 1)); - if (grad == NULL) - return NULL; - - // The shape width and height. - if (data->units == NSVG_OBJECT_SPACE) { - ox = localBounds[0]; - oy = localBounds[1]; - sw = localBounds[2] - localBounds[0]; - sh = localBounds[3] - localBounds[1]; - } - else { - ox = nsvg__actualOrigX(p); - oy = nsvg__actualOrigY(p); - sw = nsvg__actualWidth(p); - sh = nsvg__actualHeight(p); - } - sl = sqrtf(sw * sw + sh * sh) / sqrtf(2.0f); - - if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { - float x1, y1, x2, y2, dx, dy; - x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); - y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); - x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); - y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); - // Calculate transform aligned to the line - dx = x2 - x1; - dy = y2 - y1; - grad->xform[0] = dy; - grad->xform[1] = -dx; - grad->xform[2] = dx; - grad->xform[3] = dy; - grad->xform[4] = x1; - grad->xform[5] = y1; - } - else { - float cx, cy, fx, fy, r; - cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); - cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); - fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); - fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); - r = nsvg__convertToPixels(p, data->radial.r, 0, sl); - // Calculate transform aligned to the circle - grad->xform[0] = r; - grad->xform[1] = 0; - grad->xform[2] = 0; - grad->xform[3] = r; - grad->xform[4] = cx; - grad->xform[5] = cy; - grad->fx = fx / r; - grad->fy = fy / r; - } - - nsvg__xformMultiply(grad->xform, data->xform); - nsvg__xformMultiply(grad->xform, attr->xform); - - grad->spread = data->spread; - memcpy(grad->stops, stops, nstops * sizeof(NSVGgradientStop)); - grad->nstops = nstops; - - *paintType = data->type; - - return grad; -} - -static float nsvg__getAverageScale(float *t) -{ - float sx = sqrtf(t[0] * t[0] + t[2] * t[2]); - float sy = sqrtf(t[1] * t[1] + t[3] * t[3]); - return (sx + sy) * 0.5f; -} - -static void nsvg__getLocalBounds(float *bounds, NSVGshape *shape, float *xform) -{ - NSVGpath *path; - float curve[4 * 2], curveBounds[4]; - int i, first = 1; - for (path = shape->paths; path != NULL; path = path->next) { - nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); - for (i = 0; i < path->npts - 1; i += 3) { - nsvg__xformPoint( - &curve[2], &curve[3], path->pts[(i + 1) * 2], path->pts[(i + 1) * 2 + 1], xform); - nsvg__xformPoint( - &curve[4], &curve[5], path->pts[(i + 2) * 2], path->pts[(i + 2) * 2 + 1], xform); - nsvg__xformPoint( - &curve[6], &curve[7], path->pts[(i + 3) * 2], path->pts[(i + 3) * 2 + 1], xform); - nsvg__curveBounds(curveBounds, curve); - if (first) { - bounds[0] = curveBounds[0]; - bounds[1] = curveBounds[1]; - bounds[2] = curveBounds[2]; - bounds[3] = curveBounds[3]; - first = 0; - } - else { - bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); - bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); - bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); - bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); - } - curve[0] = curve[6]; - curve[1] = curve[7]; - } - } -} - -static void nsvg__addShape(NSVGparser *p) -{ - NSVGattrib *attr = nsvg__getAttr(p); - float scale = 1.0f; - NSVGshape *shape; - NSVGpath *path; - int i; - - if (p->plist == NULL) - return; - - shape = (NSVGshape *)malloc(sizeof(NSVGshape)); - if (shape == NULL) - goto error; - memset(shape, 0, sizeof(NSVGshape)); - - memcpy(shape->id, attr->id, sizeof shape->id); - /* Copy parent id from breadcrumb. */ - if (p->breadcrumb_len > 0) { - memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent); - } - else { - memcpy(shape->id_parent, attr->id, sizeof shape->id_parent); - } - - scale = nsvg__getAverageScale(attr->xform); - shape->strokeWidth = attr->strokeWidth * scale; - shape->strokeDashOffset = attr->strokeDashOffset * scale; - shape->strokeDashCount = (char)attr->strokeDashCount; - for (i = 0; i < attr->strokeDashCount; i++) - shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; - shape->strokeLineJoin = attr->strokeLineJoin; - shape->strokeLineCap = attr->strokeLineCap; - shape->miterLimit = attr->miterLimit; - shape->fillRule = attr->fillRule; - shape->opacity = attr->opacity; - - shape->paths = p->plist; - p->plist = NULL; - - // Calculate shape bounds - shape->bounds[0] = shape->paths->bounds[0]; - shape->bounds[1] = shape->paths->bounds[1]; - shape->bounds[2] = shape->paths->bounds[2]; - shape->bounds[3] = shape->paths->bounds[3]; - for (path = shape->paths->next; path != NULL; path = path->next) { - shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); - shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); - shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); - shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); - } - - // Set fill - if (attr->hasFill == 0) { - shape->fill.type = NSVG_PAINT_NONE; - } - else if (attr->hasFill == 1) { - shape->fill.type = NSVG_PAINT_COLOR; - shape->fill.color = attr->fillColor; - shape->fill.color |= (unsigned int)(attr->fillOpacity * 255) << 24; - } - else if (attr->hasFill == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->fill.gradient = nsvg__createGradient( - p, attr->fillGradient, localBounds, &shape->fill.type); - if (shape->fill.gradient == NULL) { - shape->fill.type = NSVG_PAINT_NONE; - } - } - - // Set stroke - if (attr->hasStroke == 0) { - shape->stroke.type = NSVG_PAINT_NONE; - } - else if (attr->hasStroke == 1) { - shape->stroke.type = NSVG_PAINT_COLOR; - shape->stroke.color = attr->strokeColor; - shape->stroke.color |= (unsigned int)(attr->strokeOpacity * 255) << 24; - } - else if (attr->hasStroke == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->stroke.gradient = nsvg__createGradient( - p, attr->strokeGradient, localBounds, &shape->stroke.type); - if (shape->stroke.gradient == NULL) - shape->stroke.type = NSVG_PAINT_NONE; - } - - // Set flags - shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); - - // Add to tail - if (p->image->shapes == NULL) - p->image->shapes = shape; - else - p->shapesTail->next = shape; - p->shapesTail = shape; - - return; - -error: - if (shape) - free(shape); -} - -static void nsvg__addPath(NSVGparser *p, char closed) -{ - NSVGattrib *attr = nsvg__getAttr(p); - NSVGpath *path = NULL; - float bounds[4]; - float *curve; - int i; - - if (p->npts < 4) - return; - - if (closed) - nsvg__lineTo(p, p->pts[0], p->pts[1]); - - // Expect 1 + N*3 points (N = number of cubic bezier segments). - if ((p->npts % 3) != 1) - return; - - path = (NSVGpath *)malloc(sizeof(NSVGpath)); - if (path == NULL) - goto error; - memset(path, 0, sizeof(NSVGpath)); - - path->pts = (float *)malloc(p->npts * 2 * sizeof(float)); - if (path->pts == NULL) - goto error; - path->closed = closed; - path->npts = p->npts; - - // Transform path. - for (i = 0; i < p->npts; ++i) - nsvg__xformPoint( - &path->pts[i * 2], &path->pts[i * 2 + 1], p->pts[i * 2], p->pts[i * 2 + 1], attr->xform); - - // Find bounds - for (i = 0; i < path->npts - 1; i += 3) { - curve = &path->pts[i * 2]; - nsvg__curveBounds(bounds, curve); - if (i == 0) { - path->bounds[0] = bounds[0]; - path->bounds[1] = bounds[1]; - path->bounds[2] = bounds[2]; - path->bounds[3] = bounds[3]; - } - else { - path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); - path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); - path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); - path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); - } - } - - path->next = p->plist; - p->plist = path; - - return; - -error: - if (path != NULL) { - if (path->pts != NULL) - free(path->pts); - free(path); - } -} - -// We roll our own string to float because the std library one uses locale and messes things up. -static double nsvg__atof(const char *s) -{ - char *cur = (char *)s; - char *end = NULL; - double res = 0.0, sign = 1.0; - long long intPart = 0, fracPart = 0; - char hasIntPart = 0, hasFracPart = 0; - - // Parse optional sign - if (*cur == '+') { - cur++; - } - else if (*cur == '-') { - sign = -1; - cur++; - } - - // Parse integer part - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - intPart = strtoll(cur, &end, 10); - if (cur != end) { - res = (double)intPart; - hasIntPart = 1; - cur = end; - } - } - - // Parse fractional part. - if (*cur == '.') { - cur++; // Skip '.' - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - fracPart = strtoll(cur, &end, 10); - if (cur != end) { - res += (double)fracPart / pow(10.0, (double)(end - cur)); - hasFracPart = 1; - cur = end; - } - } - } - - // A valid number should have integer or fractional part. - if (!hasIntPart && !hasFracPart) - return 0.0; - - // Parse optional exponent - if (*cur == 'e' || *cur == 'E') { - long expPart = 0; - cur++; // skip 'E' - expPart = strtol(cur, &end, 10); // Parse digit sequence with sign - if (cur != end) { - res *= pow(10.0, (double)expPart); - } - } - - return res * sign; -} - -static const char *nsvg__parseNumber(const char *s, char *it, const int size) -{ - const int last = size - 1; - int i = 0; - - // sign - if (*s == '-' || *s == '+') { - if (i < last) - it[i++] = *s; - s++; - } - // integer part - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - if (*s == '.') { - // decimal point - if (i < last) - it[i++] = *s; - s++; - // fraction part - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - } - // exponent - if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { - if (i < last) - it[i++] = *s; - s++; - if (*s == '-' || *s == '+') { - if (i < last) - it[i++] = *s; - s++; - } - while (*s && nsvg__isdigit(*s)) { - if (i < last) - it[i++] = *s; - s++; - } - } - it[i] = '\0'; - - return s; -} - -static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs) -{ - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) - s++; - if (!*s) - return s; - - /* Blender: Special case for arc command's 4th and 5th arguments. */ - if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) { - it[0] = s[0]; - it[1] = '\0'; - s++; - return s; - } - - if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { - s = nsvg__parseNumber(s, it, 64); - } - else { - // Parse command - it[0] = *s++; - it[1] = '\0'; - return s; - } - - return s; -} - -static unsigned int nsvg__parseColorHex(const char *str) -{ - unsigned int c = 0, r = 0, g = 0, b = 0; - int n = 0; - str++; // skip # - // Calculate number of characters. - while (str[n] && !nsvg__isspace(str[n])) - n++; - if (n == 6) { - sscanf(str, "%x", &c); - } - else if (n == 3) { - sscanf(str, "%x", &c); - c = (c & 0xf) | ((c & 0xf0) << 4) | ((c & 0xf00) << 8); - c |= c << 4; - } - r = (c >> 16) & 0xff; - g = (c >> 8) & 0xff; - b = c & 0xff; - return NSVG_RGB(r, g, b); -} - -static unsigned int nsvg__parseColorRGB(const char *str) -{ - int r = -1, g = -1, b = -1; - char s1[32] = "", s2[32] = ""; - sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); - if (strchr(s1, '%')) { - return NSVG_RGB((r * 255) / 100, (g * 255) / 100, (b * 255) / 100); - } - else { - return NSVG_RGB(r, g, b); - } -} - -typedef struct NSVGNamedColor { - const char *name; - unsigned int color; -} NSVGNamedColor; - -NSVGNamedColor nsvg__colors[] = { - - {"red", NSVG_RGB(255, 0, 0)}, - {"green", NSVG_RGB(0, 128, 0)}, - {"blue", NSVG_RGB(0, 0, 255)}, - {"yellow", NSVG_RGB(255, 255, 0)}, - {"cyan", NSVG_RGB(0, 255, 255)}, - {"magenta", NSVG_RGB(255, 0, 255)}, - {"black", NSVG_RGB(0, 0, 0)}, - {"grey", NSVG_RGB(128, 128, 128)}, - {"gray", NSVG_RGB(128, 128, 128)}, - {"white", NSVG_RGB(255, 255, 255)}, - -#ifdef NANOSVG_ALL_COLOR_KEYWORDS - {"aliceblue", NSVG_RGB(240, 248, 255)}, - {"antiquewhite", NSVG_RGB(250, 235, 215)}, - {"aqua", NSVG_RGB(0, 255, 255)}, - {"aquamarine", NSVG_RGB(127, 255, 212)}, - {"azure", NSVG_RGB(240, 255, 255)}, - {"beige", NSVG_RGB(245, 245, 220)}, - {"bisque", NSVG_RGB(255, 228, 196)}, - {"blanchedalmond", NSVG_RGB(255, 235, 205)}, - {"blueviolet", NSVG_RGB(138, 43, 226)}, - {"brown", NSVG_RGB(165, 42, 42)}, - {"burlywood", NSVG_RGB(222, 184, 135)}, - {"cadetblue", NSVG_RGB(95, 158, 160)}, - {"chartreuse", NSVG_RGB(127, 255, 0)}, - {"chocolate", NSVG_RGB(210, 105, 30)}, - {"coral", NSVG_RGB(255, 127, 80)}, - {"cornflowerblue", NSVG_RGB(100, 149, 237)}, - {"cornsilk", NSVG_RGB(255, 248, 220)}, - {"crimson", NSVG_RGB(220, 20, 60)}, - {"darkblue", NSVG_RGB(0, 0, 139)}, - {"darkcyan", NSVG_RGB(0, 139, 139)}, - {"darkgoldenrod", NSVG_RGB(184, 134, 11)}, - {"darkgray", NSVG_RGB(169, 169, 169)}, - {"darkgreen", NSVG_RGB(0, 100, 0)}, - {"darkgrey", NSVG_RGB(169, 169, 169)}, - {"darkkhaki", NSVG_RGB(189, 183, 107)}, - {"darkmagenta", NSVG_RGB(139, 0, 139)}, - {"darkolivegreen", NSVG_RGB(85, 107, 47)}, - {"darkorange", NSVG_RGB(255, 140, 0)}, - {"darkorchid", NSVG_RGB(153, 50, 204)}, - {"darkred", NSVG_RGB(139, 0, 0)}, - {"darksalmon", NSVG_RGB(233, 150, 122)}, - {"darkseagreen", NSVG_RGB(143, 188, 143)}, - {"darkslateblue", NSVG_RGB(72, 61, 139)}, - {"darkslategray", NSVG_RGB(47, 79, 79)}, - {"darkslategrey", NSVG_RGB(47, 79, 79)}, - {"darkturquoise", NSVG_RGB(0, 206, 209)}, - {"darkviolet", NSVG_RGB(148, 0, 211)}, - {"deeppink", NSVG_RGB(255, 20, 147)}, - {"deepskyblue", NSVG_RGB(0, 191, 255)}, - {"dimgray", NSVG_RGB(105, 105, 105)}, - {"dimgrey", NSVG_RGB(105, 105, 105)}, - {"dodgerblue", NSVG_RGB(30, 144, 255)}, - {"firebrick", NSVG_RGB(178, 34, 34)}, - {"floralwhite", NSVG_RGB(255, 250, 240)}, - {"forestgreen", NSVG_RGB(34, 139, 34)}, - {"fuchsia", NSVG_RGB(255, 0, 255)}, - {"gainsboro", NSVG_RGB(220, 220, 220)}, - {"ghostwhite", NSVG_RGB(248, 248, 255)}, - {"gold", NSVG_RGB(255, 215, 0)}, - {"goldenrod", NSVG_RGB(218, 165, 32)}, - {"greenyellow", NSVG_RGB(173, 255, 47)}, - {"honeydew", NSVG_RGB(240, 255, 240)}, - {"hotpink", NSVG_RGB(255, 105, 180)}, - {"indianred", NSVG_RGB(205, 92, 92)}, - {"indigo", NSVG_RGB(75, 0, 130)}, - {"ivory", NSVG_RGB(255, 255, 240)}, - {"khaki", NSVG_RGB(240, 230, 140)}, - {"lavender", NSVG_RGB(230, 230, 250)}, - {"lavenderblush", NSVG_RGB(255, 240, 245)}, - {"lawngreen", NSVG_RGB(124, 252, 0)}, - {"lemonchiffon", NSVG_RGB(255, 250, 205)}, - {"lightblue", NSVG_RGB(173, 216, 230)}, - {"lightcoral", NSVG_RGB(240, 128, 128)}, - {"lightcyan", NSVG_RGB(224, 255, 255)}, - {"lightgoldenrodyellow", NSVG_RGB(250, 250, 210)}, - {"lightgray", NSVG_RGB(211, 211, 211)}, - {"lightgreen", NSVG_RGB(144, 238, 144)}, - {"lightgrey", NSVG_RGB(211, 211, 211)}, - {"lightpink", NSVG_RGB(255, 182, 193)}, - {"lightsalmon", NSVG_RGB(255, 160, 122)}, - {"lightseagreen", NSVG_RGB(32, 178, 170)}, - {"lightskyblue", NSVG_RGB(135, 206, 250)}, - {"lightslategray", NSVG_RGB(119, 136, 153)}, - {"lightslategrey", NSVG_RGB(119, 136, 153)}, - {"lightsteelblue", NSVG_RGB(176, 196, 222)}, - {"lightyellow", NSVG_RGB(255, 255, 224)}, - {"lime", NSVG_RGB(0, 255, 0)}, - {"limegreen", NSVG_RGB(50, 205, 50)}, - {"linen", NSVG_RGB(250, 240, 230)}, - {"maroon", NSVG_RGB(128, 0, 0)}, - {"mediumaquamarine", NSVG_RGB(102, 205, 170)}, - {"mediumblue", NSVG_RGB(0, 0, 205)}, - {"mediumorchid", NSVG_RGB(186, 85, 211)}, - {"mediumpurple", NSVG_RGB(147, 112, 219)}, - {"mediumseagreen", NSVG_RGB(60, 179, 113)}, - {"mediumslateblue", NSVG_RGB(123, 104, 238)}, - {"mediumspringgreen", NSVG_RGB(0, 250, 154)}, - {"mediumturquoise", NSVG_RGB(72, 209, 204)}, - {"mediumvioletred", NSVG_RGB(199, 21, 133)}, - {"midnightblue", NSVG_RGB(25, 25, 112)}, - {"mintcream", NSVG_RGB(245, 255, 250)}, - {"mistyrose", NSVG_RGB(255, 228, 225)}, - {"moccasin", NSVG_RGB(255, 228, 181)}, - {"navajowhite", NSVG_RGB(255, 222, 173)}, - {"navy", NSVG_RGB(0, 0, 128)}, - {"oldlace", NSVG_RGB(253, 245, 230)}, - {"olive", NSVG_RGB(128, 128, 0)}, - {"olivedrab", NSVG_RGB(107, 142, 35)}, - {"orange", NSVG_RGB(255, 165, 0)}, - {"orangered", NSVG_RGB(255, 69, 0)}, - {"orchid", NSVG_RGB(218, 112, 214)}, - {"palegoldenrod", NSVG_RGB(238, 232, 170)}, - {"palegreen", NSVG_RGB(152, 251, 152)}, - {"paleturquoise", NSVG_RGB(175, 238, 238)}, - {"palevioletred", NSVG_RGB(219, 112, 147)}, - {"papayawhip", NSVG_RGB(255, 239, 213)}, - {"peachpuff", NSVG_RGB(255, 218, 185)}, - {"peru", NSVG_RGB(205, 133, 63)}, - {"pink", NSVG_RGB(255, 192, 203)}, - {"plum", NSVG_RGB(221, 160, 221)}, - {"powderblue", NSVG_RGB(176, 224, 230)}, - {"purple", NSVG_RGB(128, 0, 128)}, - {"rosybrown", NSVG_RGB(188, 143, 143)}, - {"royalblue", NSVG_RGB(65, 105, 225)}, - {"saddlebrown", NSVG_RGB(139, 69, 19)}, - {"salmon", NSVG_RGB(250, 128, 114)}, - {"sandybrown", NSVG_RGB(244, 164, 96)}, - {"seagreen", NSVG_RGB(46, 139, 87)}, - {"seashell", NSVG_RGB(255, 245, 238)}, - {"sienna", NSVG_RGB(160, 82, 45)}, - {"silver", NSVG_RGB(192, 192, 192)}, - {"skyblue", NSVG_RGB(135, 206, 235)}, - {"slateblue", NSVG_RGB(106, 90, 205)}, - {"slategray", NSVG_RGB(112, 128, 144)}, - {"slategrey", NSVG_RGB(112, 128, 144)}, - {"snow", NSVG_RGB(255, 250, 250)}, - {"springgreen", NSVG_RGB(0, 255, 127)}, - {"steelblue", NSVG_RGB(70, 130, 180)}, - {"tan", NSVG_RGB(210, 180, 140)}, - {"teal", NSVG_RGB(0, 128, 128)}, - {"thistle", NSVG_RGB(216, 191, 216)}, - {"tomato", NSVG_RGB(255, 99, 71)}, - {"turquoise", NSVG_RGB(64, 224, 208)}, - {"violet", NSVG_RGB(238, 130, 238)}, - {"wheat", NSVG_RGB(245, 222, 179)}, - {"whitesmoke", NSVG_RGB(245, 245, 245)}, - {"yellowgreen", NSVG_RGB(154, 205, 50)}, -#endif -}; - -static unsigned int nsvg__parseColorName(const char *str) -{ - int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); - - for (i = 0; i < ncolors; i++) { - if (strcmp(nsvg__colors[i].name, str) == 0) { - return nsvg__colors[i].color; - } - } - - return NSVG_RGB(128, 128, 128); -} - -static unsigned int nsvg__parseColor(const char *str) -{ - size_t len = 0; - while (*str == ' ') - ++str; - len = strlen(str); - if (len >= 1 && *str == '#') - return nsvg__parseColorHex(str); - else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') - return nsvg__parseColorRGB(str); - return nsvg__parseColorName(str); -} - -static float nsvg__parseOpacity(const char *str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) - val = 0.0f; - if (val > 1.0f) - val = 1.0f; - return val; -} - -static float nsvg__parseMiterLimit(const char *str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) - val = 0.0f; - return val; -} - -static int nsvg__parseUnits(const char *units) -{ - if (units[0] == 'p' && units[1] == 'x') - return NSVG_UNITS_PX; - else if (units[0] == 'p' && units[1] == 't') - return NSVG_UNITS_PT; - else if (units[0] == 'p' && units[1] == 'c') - return NSVG_UNITS_PC; - else if (units[0] == 'm' && units[1] == 'm') - return NSVG_UNITS_MM; - else if (units[0] == 'c' && units[1] == 'm') - return NSVG_UNITS_CM; - else if (units[0] == 'i' && units[1] == 'n') - return NSVG_UNITS_IN; - else if (units[0] == '%') - return NSVG_UNITS_PERCENT; - else if (units[0] == 'e' && units[1] == 'm') - return NSVG_UNITS_EM; - else if (units[0] == 'e' && units[1] == 'x') - return NSVG_UNITS_EX; - return NSVG_UNITS_USER; -} - -static int nsvg__isCoordinate(const char *s) -{ - // optional sign - if (*s == '-' || *s == '+') - s++; - // must have at least one digit, or start by a dot - return (nsvg__isdigit(*s) || *s == '.'); -} - -static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str) -{ - NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char buf[64]; - coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); - coord.value = nsvg__atof(buf); - return coord; -} - -static NSVGcoordinate nsvg__coord(float v, int units) -{ - NSVGcoordinate coord = {v, units}; - return coord; -} - -static float nsvg__parseCoordinate(NSVGparser *p, const char *str, float orig, float length) -{ - NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); - return nsvg__convertToPixels(p, coord, orig, length); -} - -static int nsvg__parseTransformArgs(const char *str, float *args, int maxNa, int *na) -{ - const char *end; - const char *ptr; - char it[64]; - - *na = 0; - ptr = str; - while (*ptr && *ptr != '(') - ++ptr; - if (*ptr == 0) - return 1; - end = ptr; - while (*end && *end != ')') - ++end; - if (*end == 0) - return 1; - - while (ptr < end) { - if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { - if (*na >= maxNa) - return 0; - ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)nsvg__atof(it); - } - else { - ++ptr; - } - } - return (int)(end - str); -} - -static int nsvg__parseMatrix(float *xform, const char *str) -{ - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, t, 6, &na); - if (na != 6) - return len; - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseTranslate(float *xform, const char *str) -{ - float args[2]; - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) - args[1] = 0.0; - - nsvg__xformSetTranslation(t, args[0], args[1]); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseScale(float *xform, const char *str) -{ - float args[2]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) - args[1] = args[0]; - nsvg__xformSetScale(t, args[0], args[1]); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseSkewX(float *xform, const char *str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewX(t, args[0] / 180.0f * NSVG_PI); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseSkewY(float *xform, const char *str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewY(t, args[0] / 180.0f * NSVG_PI); - memcpy(xform, t, sizeof(float) * 6); - return len; -} - -static int nsvg__parseRotate(float *xform, const char *str) -{ - float args[3]; - int na = 0; - float m[6]; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 3, &na); - if (na == 1) - args[1] = args[2] = 0.0f; - nsvg__xformIdentity(m); - - if (na > 1) { - nsvg__xformSetTranslation(t, -args[1], -args[2]); - nsvg__xformMultiply(m, t); - } - - nsvg__xformSetRotation(t, args[0] / 180.0f * NSVG_PI); - nsvg__xformMultiply(m, t); - - if (na > 1) { - nsvg__xformSetTranslation(t, args[1], args[2]); - nsvg__xformMultiply(m, t); - } - - memcpy(xform, m, sizeof(float) * 6); - - return len; -} - -static void nsvg__parseTransform(float *xform, const char *str) -{ - float t[6]; - int len; - nsvg__xformIdentity(xform); - while (*str) { - if (strncmp(str, "matrix", 6) == 0) - len = nsvg__parseMatrix(t, str); - else if (strncmp(str, "translate", 9) == 0) - len = nsvg__parseTranslate(t, str); - else if (strncmp(str, "scale", 5) == 0) - len = nsvg__parseScale(t, str); - else if (strncmp(str, "rotate", 6) == 0) - len = nsvg__parseRotate(t, str); - else if (strncmp(str, "skewX", 5) == 0) - len = nsvg__parseSkewX(t, str); - else if (strncmp(str, "skewY", 5) == 0) - len = nsvg__parseSkewY(t, str); - else { - ++str; - continue; - } - if (len != 0) { - str += len; - } - else { - ++str; - continue; - } - - nsvg__xformPremultiply(xform, t); - } -} - -static void nsvg__parseUrl(char *id, const char *str) -{ - int i = 0; - str += 4; // "url("; - if (*str == '#') - str++; - while (i < 63 && *str != ')') { - id[i] = *str++; - i++; - } - id[i] = '\0'; -} - -static char nsvg__parseLineCap(const char *str) -{ - if (strcmp(str, "butt") == 0) - return NSVG_CAP_BUTT; - else if (strcmp(str, "round") == 0) - return NSVG_CAP_ROUND; - else if (strcmp(str, "square") == 0) - return NSVG_CAP_SQUARE; - // TODO: handle inherit. - return NSVG_CAP_BUTT; -} - -static char nsvg__parseLineJoin(const char *str) -{ - if (strcmp(str, "miter") == 0) - return NSVG_JOIN_MITER; - else if (strcmp(str, "round") == 0) - return NSVG_JOIN_ROUND; - else if (strcmp(str, "bevel") == 0) - return NSVG_JOIN_BEVEL; - // TODO: handle inherit. - return NSVG_JOIN_MITER; -} - -static char nsvg__parseFillRule(const char *str) -{ - if (strcmp(str, "nonzero") == 0) - return NSVG_FILLRULE_NONZERO; - else if (strcmp(str, "evenodd") == 0) - return NSVG_FILLRULE_EVENODD; - // TODO: handle inherit. - return NSVG_FILLRULE_NONZERO; -} - -static const char *nsvg__getNextDashItem(const char *s, char *it) -{ - int n = 0; - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) - s++; - // Advance until whitespace, comma or end. - while (*s && (!nsvg__isspace(*s) && *s != ',')) { - if (n < 63) - it[n++] = *s; - s++; - } - it[n++] = '\0'; - return s; -} - -static int nsvg__parseStrokeDashArray(NSVGparser *p, const char *str, float *strokeDashArray) -{ - char item[64]; - int count = 0, i; - float sum = 0.0f; - - // Handle "none" - if (str[0] == 'n') - return 0; - - // Parse dashes - while (*str) { - str = nsvg__getNextDashItem(str, item); - if (!*item) - break; - if (count < NSVG_MAX_DASHES) - strokeDashArray[count++] = fabsf( - nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); - } - - for (i = 0; i < count; i++) - sum += strokeDashArray[i]; - if (sum <= 1e-6f) - count = 0; - - return count; -} - -static void nsvg__parseStyle(NSVGparser *p, const char *str); - -static int nsvg__parseAttr(NSVGparser *p, const char *name, const char *value) -{ - float xform[6]; - NSVGattrib *attr = nsvg__getAttr(p); - if (!attr) - return 0; - - if (strcmp(name, "style") == 0) { - nsvg__parseStyle(p, value); - } - else if (strcmp(name, "display") == 0) { - if (strcmp(value, "none") == 0) - attr->visible = 0; - // Don't reset ->visible on display:inline, one display:none hides the whole subtree - } - else if (strcmp(name, "fill") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasFill = 0; - } - else if (strncmp(value, "url(", 4) == 0) { - attr->hasFill = 2; - nsvg__parseUrl(attr->fillGradient, value); - } - else { - attr->hasFill = 1; - attr->fillColor = nsvg__parseColor(value); - } - } - else if (strcmp(name, "opacity") == 0) { - attr->opacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "fill-opacity") == 0) { - attr->fillOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "stroke") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasStroke = 0; - } - else if (strncmp(value, "url(", 4) == 0) { - attr->hasStroke = 2; - nsvg__parseUrl(attr->strokeGradient, value); - } - else { - attr->hasStroke = 1; - attr->strokeColor = nsvg__parseColor(value); - } - } - else if (strcmp(name, "stroke-width") == 0) { - attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "stroke-dasharray") == 0) { - attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); - } - else if (strcmp(name, "stroke-dashoffset") == 0) { - attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "stroke-opacity") == 0) { - attr->strokeOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "stroke-linecap") == 0) { - attr->strokeLineCap = nsvg__parseLineCap(value); - } - else if (strcmp(name, "stroke-linejoin") == 0) { - attr->strokeLineJoin = nsvg__parseLineJoin(value); - } - else if (strcmp(name, "stroke-miterlimit") == 0) { - attr->miterLimit = nsvg__parseMiterLimit(value); - } - else if (strcmp(name, "fill-rule") == 0) { - attr->fillRule = nsvg__parseFillRule(value); - } - else if (strcmp(name, "font-size") == 0) { - attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } - else if (strcmp(name, "transform") == 0) { - nsvg__parseTransform(xform, value); - nsvg__xformPremultiply(attr->xform, xform); - } - else if (strcmp(name, "stop-color") == 0) { - attr->stopColor = nsvg__parseColor(value); - } - else if (strcmp(name, "stop-opacity") == 0) { - attr->stopOpacity = nsvg__parseOpacity(value); - } - else if (strcmp(name, "offset") == 0) { - attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); - } - else if (strcmp(name, "id") == 0) { - strncpy(attr->id, value, 63); - attr->id[63] = '\0'; - } - else { - return 0; - } - return 1; -} - -static int nsvg__parseNameValue(NSVGparser *p, const char *start, const char *end) -{ - const char *str; - const char *val; - char name[512]; - char value[512]; - int n; - - str = start; - while (str < end && *str != ':') - ++str; - - val = str; - - // Right Trim - while (str > start && (*str == ':' || nsvg__isspace(*str))) - --str; - ++str; - - n = (int)(str - start); - if (n > 511) - n = 511; - if (n) - memcpy(name, start, n); - name[n] = 0; - - while (val < end && (*val == ':' || nsvg__isspace(*val))) - ++val; - - n = (int)(end - val); - if (n > 511) - n = 511; - if (n) - memcpy(value, val, n); - value[n] = 0; - - return nsvg__parseAttr(p, name, value); -} - -static void nsvg__parseStyle(NSVGparser *p, const char *str) -{ - const char *start; - const char *end; - - while (*str) { - // Left Trim - while (*str && nsvg__isspace(*str)) - ++str; - start = str; - while (*str && *str != ';') - ++str; - end = str; - - // Right Trim - while (end > start && (*end == ';' || nsvg__isspace(*end))) - --end; - ++end; - - nsvg__parseNameValue(p, start, end); - if (*str) - ++str; - } -} - -static void nsvg__parseAttribs(NSVGparser *p, const char **attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "style") == 0) - nsvg__parseStyle(p, attr[i + 1]); - else - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } -} - -static int nsvg__getArgsPerElement(char cmd) -{ - switch (cmd) { - case 'v': - case 'V': - case 'h': - case 'H': - return 1; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - return 2; - case 'q': - case 'Q': - case 's': - case 'S': - return 4; - case 'c': - case 'C': - return 6; - case 'a': - case 'A': - return 7; - case 'z': - case 'Z': - return 0; - } - return -1; -} - -static void nsvg__pathMoveTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } - else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__moveTo(p, *cpx, *cpy); -} - -static void nsvg__pathLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } - else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathHLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) - *cpx += args[0]; - else - *cpx = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathVLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - if (rel) - *cpy += args[0]; - else - *cpy = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathCubicBezTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x2, y2, cx1, cy1, cx2, cy2; - - if (rel) { - cx1 = *cpx + args[0]; - cy1 = *cpy + args[1]; - cx2 = *cpx + args[2]; - cy2 = *cpy + args[3]; - x2 = *cpx + args[4]; - y2 = *cpy + args[5]; - } - else { - cx1 = args[0]; - cy1 = args[1]; - cx2 = args[2]; - cy2 = args[3]; - x2 = args[4]; - y2 = args[5]; - } - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathCubicBezShortTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx2 = *cpx + args[0]; - cy2 = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } - else { - cx2 = args[0]; - cy2 = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - cx1 = 2 * x1 - *cpx2; - cy1 = 2 * y1 - *cpy2; - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx = *cpx + args[0]; - cy = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } - else { - cx = args[0]; - cy = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - // Convert to cubic bezier - cx1 = x1 + 2.0f / 3.0f * (cx - x1); - cy1 = y1 + 2.0f / 3.0f * (cy - y1); - cx2 = x2 + 2.0f / 3.0f * (cx - x2); - cy2 = y2 + 2.0f / 3.0f * (cy - y2); - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezShortTo( - NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - x2 = *cpx + args[0]; - y2 = *cpy + args[1]; - } - else { - x2 = args[0]; - y2 = args[1]; - } - - cx = 2 * x1 - *cpx2; - cy = 2 * y1 - *cpy2; - - // Convert to cubix bezier - cx1 = x1 + 2.0f / 3.0f * (cx - x1); - cy1 = y1 + 2.0f / 3.0f * (cy - y1); - cx2 = x2 + 2.0f / 3.0f * (cx - x2); - cy2 = y2 + 2.0f / 3.0f * (cy - y2); - - nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static float nsvg__sqr(float x) -{ - return x * x; -} -static float nsvg__vmag(float x, float y) -{ - return sqrtf(x * x + y * y); -} - -static float nsvg__vecrat(float ux, float uy, float vx, float vy) -{ - return (ux * vx + uy * vy) / (nsvg__vmag(ux, uy) * nsvg__vmag(vx, vy)); -} - -static float nsvg__vecang(float ux, float uy, float vx, float vy) -{ - float r = nsvg__vecrat(ux, uy, vx, vy); - if (r < -1.0f) - r = -1.0f; - if (r > 1.0f) - r = 1.0f; - return ((ux * vy < uy * vx) ? -1.0f : 1.0f) * acosf(r); -} - -static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel) -{ - // Ported from canvg (https://code.google.com/p/canvg/) - float rx, ry, rotx; - float x1, y1, x2, y2, cx, cy, dx, dy, d; - float x1p, y1p, cxp, cyp, s, sa, sb; - float ux, uy, vx, vy, a1, da; - float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; - float sinrx, cosrx; - int fa, fs; - int i, ndivs; - float hda, kappa; - - rx = fabsf(args[0]); // y radius - ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle - fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc - fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction - x1 = *cpx; // start point - y1 = *cpy; - if (rel) { // end point - x2 = *cpx + args[5]; - y2 = *cpy + args[6]; - } - else { - x2 = args[5]; - y2 = args[6]; - } - - dx = x1 - x2; - dy = y1 - y2; - d = sqrtf(dx * dx + dy * dy); - if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { - // The arc degenerates to a line - nsvg__lineTo(p, x2, y2); - *cpx = x2; - *cpy = y2; - return; - } - - sinrx = sinf(rotx); - cosrx = cosf(rotx); - - // Convert to center point parameterization. - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // 1) Compute x1', y1' - x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; - y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; - d = nsvg__sqr(x1p) / nsvg__sqr(rx) + nsvg__sqr(y1p) / nsvg__sqr(ry); - if (d > 1) { - d = sqrtf(d); - rx *= d; - ry *= d; - } - // 2) Compute cx', cy' - s = 0.0f; - sa = nsvg__sqr(rx) * nsvg__sqr(ry) - nsvg__sqr(rx) * nsvg__sqr(y1p) - - nsvg__sqr(ry) * nsvg__sqr(x1p); - sb = nsvg__sqr(rx) * nsvg__sqr(y1p) + nsvg__sqr(ry) * nsvg__sqr(x1p); - if (sa < 0.0f) - sa = 0.0f; - if (sb > 0.0f) - s = sqrtf(sa / sb); - if (fa == fs) - s = -s; - cxp = s * rx * y1p / ry; - cyp = s * -ry * x1p / rx; - - // 3) Compute cx,cy from cx',cy' - cx = (x1 + x2) / 2.0f + cosrx * cxp - sinrx * cyp; - cy = (y1 + y2) / 2.0f + sinrx * cxp + cosrx * cyp; - - // 4) Calculate theta1, and delta theta. - ux = (x1p - cxp) / rx; - uy = (y1p - cyp) / ry; - vx = (-x1p - cxp) / rx; - vy = (-y1p - cyp) / ry; - a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle - da = nsvg__vecang(ux, uy, vx, vy); // Delta angle - - // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; - // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - - if (fs == 0 && da > 0) - da -= 2 * NSVG_PI; - else if (fs == 1 && da < 0) - da += 2 * NSVG_PI; - - // Approximate the arc using cubic spline segments. - t[0] = cosrx; - t[1] = sinrx; - t[2] = -sinrx; - t[3] = cosrx; - t[4] = cx; - t[5] = cy; - - // Split arc into max 90 degree segments. - // The loop assumes an iteration per end point (including start and end), this +1. - ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f); - hda = (da / (float)ndivs) / 2.0f; - // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) - if ((hda < 1e-3f) && (hda > -1e-3f)) - hda *= 0.5f; - else - hda = (1.0f - cosf(hda)) / sinf(hda); - kappa = fabsf(4.0f / 3.0f * hda); - if (da < 0.0f) - kappa = -kappa; - - for (i = 0; i <= ndivs; i++) { - a = a1 + da * ((float)i / (float)ndivs); - dx = cosf(a); - dy = sinf(a); - nsvg__xformPoint(&x, &y, dx * rx, dy * ry, t); // position - nsvg__xformVec(&tanx, &tany, -dy * rx * kappa, dx * ry * kappa, t); // tangent - if (i > 0) - nsvg__cubicBezTo(p, px + ptanx, py + ptany, x - tanx, y - tany, x, y); - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - *cpx = x2; - *cpy = y2; -} - -static void nsvg__parsePath(NSVGparser *p, const char **attr) -{ - const char *s = NULL; - char cmd = '\0'; - float args[10]; - int nargs; - int rargs = 0; - char initPoint; - float cpx, cpy, cpx2, cpy2; - const char *tmp[4]; - char closedFlag; - int i; - char item[64]; - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "d") == 0) { - s = attr[i + 1]; - } - else { - tmp[0] = attr[i]; - tmp[1] = attr[i + 1]; - tmp[2] = 0; - tmp[3] = 0; - nsvg__parseAttribs(p, tmp); - } - } - - if (s) { - nsvg__resetPath(p); - cpx = 0; - cpy = 0; - cpx2 = 0; - cpy2 = 0; - initPoint = 0; - closedFlag = 0; - nargs = 0; - - while (*s) { - s = nsvg__getNextPathItem(s, item, cmd, nargs); - if (!*item) - break; - if (cmd != '\0' && nsvg__isCoordinate(item)) { - if (nargs < 10) - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= rargs) { - switch (cmd) { - case 'm': - case 'M': - nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); - // Moveto can be followed by multiple coordinate pairs, - // which should be treated as linetos. - cmd = (cmd == 'm') ? 'l' : 'L'; - rargs = nsvg__getArgsPerElement(cmd); - cpx2 = cpx; - cpy2 = cpy; - initPoint = 1; - break; - case 'l': - case 'L': - nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'H': - case 'h': - nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'V': - case 'v': - nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - case 'C': - case 'c': - nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); - break; - case 'S': - case 's': - nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); - break; - case 'Q': - case 'q': - nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); - break; - case 'T': - case 't': - nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); - break; - case 'A': - case 'a': - nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; - cpy2 = cpy; - break; - default: - if (nargs >= 2) { - cpx = args[nargs - 2]; - cpy = args[nargs - 1]; - cpx2 = cpx; - cpy2 = cpy; - } - break; - } - nargs = 0; - } - } - else { - cmd = item[0]; - if (cmd == 'M' || cmd == 'm') { - // Commit path. - if (p->npts > 0) - nsvg__addPath(p, closedFlag); - // Start new subpath. - nsvg__resetPath(p); - closedFlag = 0; - nargs = 0; - } - else if (initPoint == 0) { - // Do not allow other commands until initial point has been set (moveTo called once). - cmd = '\0'; - } - if (cmd == 'Z' || cmd == 'z') { - closedFlag = 1; - // Commit path. - if (p->npts > 0) { - // Move current point to first point - cpx = p->pts[0]; - cpy = p->pts[1]; - cpx2 = cpx; - cpy2 = cpy; - nsvg__addPath(p, closedFlag); - } - // Start new subpath. - nsvg__resetPath(p); - nsvg__moveTo(p, cpx, cpy); - closedFlag = 0; - nargs = 0; - } - rargs = nsvg__getArgsPerElement(cmd); - if (rargs == -1) { - // Command not recognized - cmd = '\0'; - rargs = 0; - } - } - } - // Commit path. - if (p->npts) - nsvg__addPath(p, closedFlag); - } - - nsvg__addShape(p); -} - -static void nsvg__parseRect(NSVGparser *p, const char **attr) -{ - float x = 0.0f; - float y = 0.0f; - float w = 0.0f; - float h = 0.0f; - float rx = -1.0f; // marks not set - float ry = -1.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x") == 0) - x = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y") == 0) - y = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "width") == 0) - w = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p)); - if (strcmp(attr[i], "height") == 0) - h = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) - rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) - ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx < 0.0f && ry > 0.0f) - rx = ry; - if (ry < 0.0f && rx > 0.0f) - ry = rx; - if (rx < 0.0f) - rx = 0.0f; - if (ry < 0.0f) - ry = 0.0f; - if (rx > w / 2.0f) - rx = w / 2.0f; - if (ry > h / 2.0f) - ry = h / 2.0f; - - if (w != 0.0f && h != 0.0f) { - nsvg__resetPath(p); - - if (rx < 0.00001f || ry < 0.0001f) { - nsvg__moveTo(p, x, y); - nsvg__lineTo(p, x + w, y); - nsvg__lineTo(p, x + w, y + h); - nsvg__lineTo(p, x, y + h); - } - else { - // Rounded rectangle - nsvg__moveTo(p, x + rx, y); - nsvg__lineTo(p, x + w - rx, y); - nsvg__cubicBezTo(p, - x + w - rx * (1 - NSVG_KAPPA90), - y, - x + w, - y + ry * (1 - NSVG_KAPPA90), - x + w, - y + ry); - nsvg__lineTo(p, x + w, y + h - ry); - nsvg__cubicBezTo(p, - x + w, - y + h - ry * (1 - NSVG_KAPPA90), - x + w - rx * (1 - NSVG_KAPPA90), - y + h, - x + w - rx, - y + h); - nsvg__lineTo(p, x + rx, y + h); - nsvg__cubicBezTo(p, - x + rx * (1 - NSVG_KAPPA90), - y + h, - x, - y + h - ry * (1 - NSVG_KAPPA90), - x, - y + h - ry); - nsvg__lineTo(p, x, y + ry); - nsvg__cubicBezTo( - p, x, y + ry * (1 - NSVG_KAPPA90), x + rx * (1 - NSVG_KAPPA90), y, x + rx, y); - } - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseCircle(NSVGparser *p, const char **attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float r = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) - cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) - cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "r") == 0) - r = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualLength(p))); - } - } - - if (r > 0.0f) { - nsvg__resetPath(p); - - nsvg__moveTo(p, cx + r, cy); - nsvg__cubicBezTo(p, cx + r, cy + r * NSVG_KAPPA90, cx + r * NSVG_KAPPA90, cy + r, cx, cy + r); - nsvg__cubicBezTo(p, cx - r * NSVG_KAPPA90, cy + r, cx - r, cy + r * NSVG_KAPPA90, cx - r, cy); - nsvg__cubicBezTo(p, cx - r, cy - r * NSVG_KAPPA90, cx - r * NSVG_KAPPA90, cy - r, cx, cy - r); - nsvg__cubicBezTo(p, cx + r * NSVG_KAPPA90, cy - r, cx + r, cy - r * NSVG_KAPPA90, cx + r, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseEllipse(NSVGparser *p, const char **attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float rx = 0.0f; - float ry = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) - cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) - cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) - rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) - ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx > 0.0f && ry > 0.0f) { - - nsvg__resetPath(p); - - nsvg__moveTo(p, cx + rx, cy); - nsvg__cubicBezTo( - p, cx + rx, cy + ry * NSVG_KAPPA90, cx + rx * NSVG_KAPPA90, cy + ry, cx, cy + ry); - nsvg__cubicBezTo( - p, cx - rx * NSVG_KAPPA90, cy + ry, cx - rx, cy + ry * NSVG_KAPPA90, cx - rx, cy); - nsvg__cubicBezTo( - p, cx - rx, cy - ry * NSVG_KAPPA90, cx - rx * NSVG_KAPPA90, cy - ry, cx, cy - ry); - nsvg__cubicBezTo( - p, cx + rx * NSVG_KAPPA90, cy - ry, cx + rx, cy - ry * NSVG_KAPPA90, cx + rx, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseLine(NSVGparser *p, const char **attr) -{ - float x1 = 0.0; - float y1 = 0.0; - float x2 = 0.0; - float y2 = 0.0; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x1") == 0) - x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y1") == 0) - y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "x2") == 0) - x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y2") == 0) - y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - } - } - - nsvg__resetPath(p); - - nsvg__moveTo(p, x1, y1); - nsvg__lineTo(p, x2, y2); - - nsvg__addPath(p, 0); - - nsvg__addShape(p); -} - -static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag) -{ - int i; - const char *s; - float args[2]; - int nargs, npts = 0; - char item[64]; - - nsvg__resetPath(p); - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "points") == 0) { - s = attr[i + 1]; - nargs = 0; - while (*s) { - s = nsvg__getNextPathItem(s, item, '\0', nargs); - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= 2) { - if (npts == 0) - nsvg__moveTo(p, args[0], args[1]); - else - nsvg__lineTo(p, args[0], args[1]); - nargs = 0; - npts++; - } - } - } - } - } - - nsvg__addPath(p, (char)closeFlag); - - nsvg__addShape(p); -} - -static void nsvg__parseSVG(NSVGparser *p, const char **attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "width") == 0) { - p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } - else if (strcmp(attr[i], "height") == 0) { - p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } - else if (strcmp(attr[i], "viewBox") == 0) { - const char *s = attr[i + 1]; - char buf[64]; - s = nsvg__parseNumber(s, buf, 64); - p->viewMinx = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewMiny = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewWidth = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) - s++; - if (!*s) - return; - s = nsvg__parseNumber(s, buf, 64); - p->viewHeight = nsvg__atof(buf); - } - else if (strcmp(attr[i], "preserveAspectRatio") == 0) { - if (strstr(attr[i + 1], "none") != 0) { - // No uniform scaling - p->alignType = NSVG_ALIGN_NONE; - } - else { - // Parse X align - if (strstr(attr[i + 1], "xMin") != 0) - p->alignX = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "xMid") != 0) - p->alignX = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "xMax") != 0) - p->alignX = NSVG_ALIGN_MAX; - // Parse X align - if (strstr(attr[i + 1], "yMin") != 0) - p->alignY = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "yMid") != 0) - p->alignY = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "yMax") != 0) - p->alignY = NSVG_ALIGN_MAX; - // Parse meet/slice - p->alignType = NSVG_ALIGN_MEET; - if (strstr(attr[i + 1], "slice") != 0) - p->alignType = NSVG_ALIGN_SLICE; - } - } - } - } -} - -static void nsvg__parseGradient(NSVGparser *p, const char **attr, char type) -{ - int i; - NSVGgradientData *grad = (NSVGgradientData *)malloc(sizeof(NSVGgradientData)); - if (grad == NULL) - return; - memset(grad, 0, sizeof(NSVGgradientData)); - grad->units = NSVG_OBJECT_SPACE; - grad->type = type; - if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { - grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); - grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - } - else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { - grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - } - - nsvg__xformIdentity(grad->xform); - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "id") == 0) { - strncpy(grad->id, attr[i + 1], 63); - grad->id[63] = '\0'; - } - else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "gradientUnits") == 0) { - if (strcmp(attr[i + 1], "objectBoundingBox") == 0) - grad->units = NSVG_OBJECT_SPACE; - else - grad->units = NSVG_USER_SPACE; - } - else if (strcmp(attr[i], "gradientTransform") == 0) { - nsvg__parseTransform(grad->xform, attr[i + 1]); - } - else if (strcmp(attr[i], "cx") == 0) { - grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "cy") == 0) { - grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "r") == 0) { - grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "fx") == 0) { - grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "fy") == 0) { - grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "x1") == 0) { - grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "y1") == 0) { - grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "x2") == 0) { - grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "y2") == 0) { - grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } - else if (strcmp(attr[i], "spreadMethod") == 0) { - if (strcmp(attr[i + 1], "pad") == 0) - grad->spread = NSVG_SPREAD_PAD; - else if (strcmp(attr[i + 1], "reflect") == 0) - grad->spread = NSVG_SPREAD_REFLECT; - else if (strcmp(attr[i + 1], "repeat") == 0) - grad->spread = NSVG_SPREAD_REPEAT; - } - else if (strcmp(attr[i], "xlink:href") == 0) { - const char *href = attr[i + 1]; - strncpy(grad->ref, href + 1, 62); - grad->ref[62] = '\0'; - } - } - } - - grad->next = p->gradients; - p->gradients = grad; -} - -static void nsvg__parseGradientStop(NSVGparser *p, const char **attr) -{ - NSVGattrib *curAttr = nsvg__getAttr(p); - NSVGgradientData *grad; - NSVGgradientStop *stop; - int i, idx; - - curAttr->stopOffset = 0; - curAttr->stopColor = 0; - curAttr->stopOpacity = 1.0f; - - for (i = 0; attr[i]; i += 2) { - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } - - // Add stop to the last gradient. - grad = p->gradients; - if (grad == NULL) - return; - - grad->nstops++; - grad->stops = (NSVGgradientStop *)realloc(grad->stops, sizeof(NSVGgradientStop) * grad->nstops); - if (grad->stops == NULL) - return; - - // Insert - idx = grad->nstops - 1; - for (i = 0; i < grad->nstops - 1; i++) { - if (curAttr->stopOffset < grad->stops[i].offset) { - idx = i; - break; - } - } - if (idx != grad->nstops - 1) { - for (i = grad->nstops - 1; i > idx; i--) - grad->stops[i] = grad->stops[i - 1]; - } - - stop = &grad->stops[idx]; - stop->color = curAttr->stopColor; - stop->color |= (unsigned int)(curAttr->stopOpacity * 255) << 24; - stop->offset = curAttr->stopOffset; -} - -static void nsvg__startElement(void *ud, const char *el, const char **attr) -{ - NSVGparser *p = (NSVGparser *)ud; - - if (p->defsFlag) { - // Skip everything but gradients in defs - if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } - else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } - else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - return; - } - - if (strcmp(el, "g") == 0) { - nsvg__pushAttr(p); - nsvg__parseAttribs(p, attr); - - /* Save the breadcrumb of groups. */ - if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) { - NSVGattrib *attr_id = nsvg__getAttr(p); - memcpy( - p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len])); - p->breadcrumb_len++; - } - } - else if (strcmp(el, "path") == 0) { - if (p->pathFlag) // Do not allow nested paths. - return; - nsvg__pushAttr(p); - nsvg__parsePath(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "rect") == 0) { - nsvg__pushAttr(p); - nsvg__parseRect(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "circle") == 0) { - nsvg__pushAttr(p); - nsvg__parseCircle(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "ellipse") == 0) { - nsvg__pushAttr(p); - nsvg__parseEllipse(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "line") == 0) { - nsvg__pushAttr(p); - nsvg__parseLine(p, attr); - nsvg__popAttr(p); - } - else if (strcmp(el, "polyline") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 0); - nsvg__popAttr(p); - } - else if (strcmp(el, "polygon") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 1); - nsvg__popAttr(p); - } - else if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } - else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } - else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - else if (strcmp(el, "defs") == 0) { - p->defsFlag = 1; - } - else if (strcmp(el, "svg") == 0) { - nsvg__parseSVG(p, attr); - } -} - -static void nsvg__endElement(void *ud, const char *el) -{ - NSVGparser *p = (NSVGparser *)ud; - - if (strcmp(el, "g") == 0) { - /* Remove the breadcrumb level. */ - if (p->breadcrumb_len > 0) { - p->breadcrumb[p->breadcrumb_len - 1][0] = '\0'; - p->breadcrumb_len--; - } - - nsvg__popAttr(p); - } - else if (strcmp(el, "path") == 0) { - p->pathFlag = 0; - } - else if (strcmp(el, "defs") == 0) { - p->defsFlag = 0; - } -} - -static void nsvg__content(void *ud, const char *s) -{ - NSVG_NOTUSED(ud); - NSVG_NOTUSED(s); - // empty -} - -static void nsvg__imageBounds(NSVGparser *p, float *bounds) -{ - NSVGshape *shape; - shape = p->image->shapes; - if (shape == NULL) { - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; - return; - } - bounds[0] = shape->bounds[0]; - bounds[1] = shape->bounds[1]; - bounds[2] = shape->bounds[2]; - bounds[3] = shape->bounds[3]; - for (shape = shape->next; shape != NULL; shape = shape->next) { - bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); - bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - } -} - -static float nsvg__viewAlign(float content, float container, int type) -{ - if (type == NSVG_ALIGN_MIN) - return 0; - else if (type == NSVG_ALIGN_MAX) - return container - content; - // mid - return (container - content) * 0.5f; -} - -static void nsvg__scaleGradient(NSVGgradient *grad, float tx, float ty, float sx, float sy) -{ - float t[6]; - nsvg__xformSetTranslation(t, tx, ty); - nsvg__xformMultiply(grad->xform, t); - - nsvg__xformSetScale(t, sx, sy); - nsvg__xformMultiply(grad->xform, t); -} - -static void nsvg__scaleToViewbox(NSVGparser *p, const char *units) -{ - NSVGshape *shape; - NSVGpath *path; - float tx, ty, sx, sy, us, bounds[4], t[6], avgs; - int i; - float *pt; - - // Guess image size if not set completely. - nsvg__imageBounds(p, bounds); - - if (p->viewWidth == 0) { - if (p->image->width > 0) { - p->viewWidth = p->image->width; - } - else { - p->viewMinx = bounds[0]; - p->viewWidth = bounds[2] - bounds[0]; - } - } - if (p->viewHeight == 0) { - if (p->image->height > 0) { - p->viewHeight = p->image->height; - } - else { - p->viewMiny = bounds[1]; - p->viewHeight = bounds[3] - bounds[1]; - } - } - if (p->image->width == 0) - p->image->width = p->viewWidth; - if (p->image->height == 0) - p->image->height = p->viewHeight; - - tx = -p->viewMinx; - ty = -p->viewMiny; - sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; - sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; - // Unit scaling - us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); - - // Fix aspect ratio - if (p->alignType == NSVG_ALIGN_MEET) { - // fit whole image into viewbox - sx = sy = nsvg__minf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; - } - else if (p->alignType == NSVG_ALIGN_SLICE) { - // fill whole viewbox with image - sx = sy = nsvg__maxf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy; - } - - // Transform - sx *= us; - sy *= us; - avgs = (sx + sy) / 2.0f; - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - shape->bounds[0] = (shape->bounds[0] + tx) * sx; - shape->bounds[1] = (shape->bounds[1] + ty) * sy; - shape->bounds[2] = (shape->bounds[2] + tx) * sx; - shape->bounds[3] = (shape->bounds[3] + ty) * sy; - for (path = shape->paths; path != NULL; path = path->next) { - path->bounds[0] = (path->bounds[0] + tx) * sx; - path->bounds[1] = (path->bounds[1] + ty) * sy; - path->bounds[2] = (path->bounds[2] + tx) * sx; - path->bounds[3] = (path->bounds[3] + ty) * sy; - for (i = 0; i < path->npts; i++) { - pt = &path->pts[i * 2]; - pt[0] = (pt[0] + tx) * sx; - pt[1] = (pt[1] + ty) * sy; - } - } - - if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || - shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->fill.gradient, tx, ty, sx, sy); - memcpy(t, shape->fill.gradient->xform, sizeof(float) * 6); - nsvg__xformInverse(shape->fill.gradient->xform, t); - } - if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || - shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->stroke.gradient, tx, ty, sx, sy); - memcpy(t, shape->stroke.gradient->xform, sizeof(float) * 6); - nsvg__xformInverse(shape->stroke.gradient->xform, t); - } - - shape->strokeWidth *= avgs; - shape->strokeDashOffset *= avgs; - for (i = 0; i < shape->strokeDashCount; i++) - shape->strokeDashArray[i] *= avgs; - } -} - -NSVGimage *nsvgParse(char *input, const char *units, float dpi) -{ - NSVGparser *p; - NSVGimage *ret = 0; - - p = nsvg__createParser(); - if (p == NULL) { - return NULL; - } - p->dpi = dpi; - - nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - - // Scale to viewBox - nsvg__scaleToViewbox(p, units); - - ret = p->image; - p->image = NULL; - - nsvg__deleteParser(p); - - return ret; -} - -NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi) -{ - FILE *fp = NULL; - size_t size; - char *data = NULL; - NSVGimage *image = NULL; - - fp = fopen(filename, "rb"); - if (!fp) - goto error; - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - data = (char *)malloc(size + 1); - if (data == NULL) - goto error; - if (fread(data, 1, size, fp) != size) - goto error; - data[size] = '\0'; // Must be null terminated. - fclose(fp); - image = nsvgParse(data, units, dpi); - free(data); - - return image; - -error: - if (fp) - fclose(fp); - if (data) - free(data); - if (image) - nsvgDelete(image); - return NULL; -} - -NSVGpath *nsvgDuplicatePath(NSVGpath *p) -{ - NSVGpath *res = NULL; - - if (p == NULL) - return NULL; - - res = (NSVGpath *)malloc(sizeof(NSVGpath)); - if (res == NULL) - goto error; - memset(res, 0, sizeof(NSVGpath)); - - res->pts = (float *)malloc(p->npts * 2 * sizeof(float)); - if (res->pts == NULL) - goto error; - memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); - res->npts = p->npts; - - memcpy(res->bounds, p->bounds, sizeof(p->bounds)); - - res->closed = p->closed; - - return res; - -error: - if (res != NULL) { - free(res->pts); - free(res); - } - return NULL; -} - -void nsvgDelete(NSVGimage *image) -{ - NSVGshape *snext, *shape; - if (image == NULL) - return; - shape = image->shapes; - while (shape != NULL) { - snext = shape->next; - nsvg__deletePaths(shape->paths); - nsvg__deletePaint(&shape->fill); - nsvg__deletePaint(&shape->stroke); - free(shape); - shape = snext; - } - free(image); -} - -#endif diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt index 6ea30f48a13..5499fe36898 100644 --- a/source/blender/io/usd/CMakeLists.txt +++ b/source/blender/io/usd/CMakeLists.txt @@ -56,7 +56,9 @@ set(INC_SYS ) set(SRC - intern/usd_capi.cc + intern/usd_capi_export.cc + intern/usd_capi_import.cc + intern/usd_common.cc intern/usd_hierarchy_iterator.cc intern/usd_writer_abstract.cc intern/usd_writer_camera.cc @@ -66,7 +68,21 @@ set(SRC intern/usd_writer_metaball.cc intern/usd_writer_transform.cc + intern/usd_reader_camera.cc + intern/usd_reader_curve.cc + intern/usd_reader_geom.cc + intern/usd_reader_light.cc + intern/usd_reader_material.cc + intern/usd_reader_mesh.cc + intern/usd_reader_nurbs.cc + intern/usd_reader_prim.cc + intern/usd_reader_stage.cc + intern/usd_reader_xform.cc + intern/usd_reader_volume.cc + usd.h + + intern/usd_common.h intern/usd_exporter_context.h intern/usd_hierarchy_iterator.h intern/usd_writer_abstract.h @@ -76,6 +92,18 @@ set(SRC intern/usd_writer_mesh.h intern/usd_writer_metaball.h intern/usd_writer_transform.h + + intern/usd_reader_camera.h + intern/usd_reader_curve.h + intern/usd_reader_geom.h + intern/usd_reader_light.h + intern/usd_reader_material.h + intern/usd_reader_mesh.h + intern/usd_reader_nurbs.h + intern/usd_reader_prim.h + intern/usd_reader_stage.h + intern/usd_reader_xform.h + intern/usd_reader_volume.h ) set(LIB diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi_export.cc index dc2b46e5cea..efa31df25c1 100644 --- a/source/blender/io/usd/intern/usd_capi.cc +++ b/source/blender/io/usd/intern/usd_capi_export.cc @@ -18,6 +18,7 @@ */ #include "usd.h" +#include "usd_common.h" #include "usd_hierarchy_iterator.h" #include <pxr/base/plug/registry.h> @@ -59,21 +60,6 @@ struct ExportJobData { bool export_ok; }; -static void ensure_usd_plugin_path_registered() -{ - static bool plugin_path_registered = false; - if (plugin_path_registered) { - return; - } - plugin_path_registered = true; - - /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' - * does not exist, the USD library will not be able to read or write any files. */ - const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"); - /* The trailing slash indicates to the USD library that the path is a directory. */ - pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/"); -} - static void export_startjob(void *customdata, /* Cannot be const, this function implements wm_jobs_start_callback. * NOLINTNEXTLINE: readability-non-const-parameter. */ @@ -116,7 +102,7 @@ static void export_startjob(void *customdata, usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, - pxr::VtValue(scene->unit.scale_length)); + static_cast<double>(scene->unit.scale_length)); usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") + BKE_blender_version_string()); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc new file mode 100644 index 00000000000..789ff20ba82 --- /dev/null +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -0,0 +1,577 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ + +#include "IO_types.h" +#include "usd.h" +#include "usd_common.h" +#include "usd_hierarchy_iterator.h" +#include "usd_reader_geom.h" +#include "usd_reader_prim.h" +#include "usd_reader_stage.h" + +#include "BKE_appdir.h" +#include "BKE_blender_version.h" +#include "BKE_cachefile.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_world.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_math_matrix.h" +#include "BLI_math_rotation.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" +#include "DNA_node_types.h" +#include "DNA_scene_types.h" +#include "DNA_world_types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include <pxr/usd/usd/stage.h> +#include <pxr/usd/usdGeom/metrics.h> +#include <pxr/usd/usdGeom/scope.h> +#include <pxr/usd/usdGeom/tokens.h> +#include <pxr/usd/usdGeom/xformCommonAPI.h> + +#include <iostream> + +namespace blender::io::usd { + +static CacheArchiveHandle *handle_from_stage_reader(USDStageReader *reader) +{ + return reinterpret_cast<CacheArchiveHandle *>(reader); +} + +static USDStageReader *stage_reader_from_handle(CacheArchiveHandle *handle) +{ + return reinterpret_cast<USDStageReader *>(handle); +} + +static bool gather_objects_paths(const pxr::UsdPrim &object, ListBase *object_paths) +{ + if (!object.IsValid()) { + return false; + } + + for (const pxr::UsdPrim &childPrim : object.GetChildren()) { + gather_objects_paths(childPrim, object_paths); + } + + void *usd_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath"); + CacheObjectPath *usd_path = static_cast<CacheObjectPath *>(usd_path_void); + + BLI_strncpy(usd_path->path, object.GetPrimPath().GetString().c_str(), sizeof(usd_path->path)); + BLI_addtail(object_paths, usd_path); + + return true; +} + +/* Update the given import settings with the global rotation matrix to orient + * imported objects with Z-up, if necessary */ +static void convert_to_z_up(pxr::UsdStageRefPtr stage, ImportSettings *r_settings) +{ + if (!stage || pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z) { + return; + } + + if (!r_settings) { + return; + } + + r_settings->do_convert_mat = true; + + /* Rotate 90 degrees about the X-axis. */ + float rmat[3][3]; + float axis[3] = {1.0f, 0.0f, 0.0f}; + axis_angle_normalized_to_mat3(rmat, axis, M_PI / 2.0f); + + unit_m4(r_settings->conversion_mat); + copy_m4_m3(r_settings->conversion_mat, rmat); +} + +enum { + USD_NO_ERROR = 0, + USD_ARCHIVE_FAIL, +}; + +struct ImportJobData { + Main *bmain; + Scene *scene; + ViewLayer *view_layer; + wmWindowManager *wm; + + char filename[1024]; + USDImportParams params; + ImportSettings settings; + + USDStageReader *archive; + + short *stop; + short *do_update; + float *progress; + + char error_code; + bool was_canceled; + bool import_ok; +}; + +static void import_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ImportJobData *data = static_cast<ImportJobData *>(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + data->was_canceled = false; + data->archive = nullptr; + + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + if (data->params.create_collection) { + char display_name[1024]; + BLI_path_to_display_name( + display_name, strlen(data->filename), BLI_path_basename(data->filename)); + Collection *import_collection = BKE_collection_add( + data->bmain, data->scene->master_collection, display_name); + id_fake_user_set(&import_collection->id); + + DEG_id_tag_update(&import_collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(data->bmain); + + WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr); + + data->view_layer->active_collection = BKE_layer_collection_first_from_scene_collection( + data->view_layer, import_collection); + } + + BLI_path_abs(data->filename, BKE_main_blendfile_path_from_global()); + + CacheFile *cache_file = static_cast<CacheFile *>( + BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + + /* Decrement the ID ref-count because it is going to be incremented for each + * modifier and constraint that it will be attached to, so since currently + * it is not used by anyone, its use count will off by one. */ + id_us_min(&cache_file->id); + + cache_file->is_sequence = data->params.is_sequence; + cache_file->scale = data->params.scale; + STRNCPY(cache_file->filepath, data->filename); + + data->settings.cache_file = cache_file; + + *data->do_update = true; + *data->progress = 0.05f; + + if (G.is_break) { + data->was_canceled = true; + return; + } + + *data->do_update = true; + *data->progress = 0.1f; + + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filename); + + if (!stage) { + WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filename); + data->import_ok = false; + return; + } + + convert_to_z_up(stage, &data->settings); + + /* Set up the stage for animated data. */ + if (data->params.set_frame_range) { + data->scene->r.sfra = stage->GetStartTimeCode(); + data->scene->r.efra = stage->GetEndTimeCode(); + } + + *data->progress = 0.15f; + + USDStageReader *archive = new USDStageReader(stage, data->params, data->settings); + + data->archive = archive; + + archive->collect_readers(data->bmain); + + *data->progress = 0.2f; + + const float size = static_cast<float>(archive->readers().size()); + size_t i = 0; + + /* Setup parenthood */ + + for (USDPrimReader *reader : archive->readers()) { + + if (!reader) { + continue; + } + + Object *ob = reader->object(); + + reader->read_object_data(data->bmain, 0.0); + + USDPrimReader *parent = reader->parent(); + + if (parent == nullptr) { + ob->parent = nullptr; + } + else { + ob->parent = parent->object(); + } + + *data->progress = 0.2f + 0.8f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_canceled = true; + return; + } + } + + data->import_ok = !data->was_canceled; + + *progress = 1.0f; + *do_update = true; +} + +static void import_endjob(void *customdata) +{ + ImportJobData *data = static_cast<ImportJobData *>(customdata); + + /* Delete objects on cancellation. */ + if (data->was_canceled && data->archive) { + + for (USDPrimReader *reader : data->archive->readers()) { + + if (!reader) { + continue; + } + + /* It's possible that cancellation occurred between the creation of + * the reader and the creation of the Blender object. */ + if (Object *ob = reader->object()) { + BKE_id_free_us(data->bmain, ob); + } + } + } + else if (data->archive) { + /* Add object to scene. */ + Base *base; + LayerCollection *lc; + ViewLayer *view_layer = data->view_layer; + + BKE_view_layer_base_deselect_all(view_layer); + + lc = BKE_layer_collection_get_active(view_layer); + + for (USDPrimReader *reader : data->archive->readers()) { + + if (!reader) { + continue; + } + + Object *ob = reader->object(); + + if (!ob) { + continue; + } + + BKE_collection_object_add(data->bmain, lc->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + + DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update_ex(data->bmain, + &ob->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | + ID_RECALC_BASE_FLAGS); + } + + DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS); + DEG_relations_tag_update(data->bmain); + } + + WM_set_locked_interface(data->wm, false); + + switch (data->error_code) { + default: + case USD_NO_ERROR: + data->import_ok = !data->was_canceled; + break; + case USD_ARCHIVE_FAIL: + WM_report(RPT_ERROR, "Could not open USD archive for reading! See console for detail."); + break; + } + + WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); +} + +static void import_freejob(void *user_data) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + + delete data->archive; + delete data; +} + +} // namespace blender::io::usd + +using namespace blender::io::usd; + +bool USD_import(struct bContext *C, + const char *filepath, + const USDImportParams *params, + bool as_background_job) +{ + blender::io::usd::ensure_usd_plugin_path_registered(); + + /* Using new here since `MEM_*` functions do not call constructor to properly initialize data. */ + ImportJobData *job = new ImportJobData(); + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + job->view_layer = CTX_data_view_layer(C); + job->wm = CTX_wm_manager(C); + job->import_ok = false; + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scale = params->scale; + job->settings.sequence_offset = params->offset; + job->settings.is_sequence = params->is_sequence; + job->settings.sequence_len = params->sequence_len; + job->settings.validate_meshes = params->validate_meshes; + job->settings.sequence_len = params->sequence_len; + job->error_code = USD_NO_ERROR; + job->was_canceled = false; + job->archive = nullptr; + + job->params = *params; + + G.is_break = false; + + bool import_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "USD Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE, NC_SCENE); + WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while importing. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + import_startjob(job, &stop, &do_update, &progress); + import_endjob(job); + import_ok = job->import_ok; + + import_freejob(job); + } + + return import_ok; +} + +/* TODO(makowalski): Extend this function with basic validation that the + * USD reader is compatible with the type of the given (currently unused) 'ob' + * Object parameter, similar to the logic in get_abc_reader() in the + * Alembic importer code. */ +static USDPrimReader *get_usd_reader(CacheReader *reader, Object * /* ob */, const char **err_str) +{ + USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader); + pxr::UsdPrim iobject = usd_reader->prim(); + + if (!iobject.IsValid()) { + *err_str = "Invalid object: verify object path"; + return nullptr; + } + + return usd_reader; +} + +struct Mesh *USD_read_mesh(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str, + const int read_flag) +{ + USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str)); + + if (usd_reader == nullptr) { + return nullptr; + } + + return usd_reader->read_mesh(existing_mesh, time, read_flag, err_str); +} + +bool USD_mesh_topology_changed( + CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) +{ + USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str)); + + if (usd_reader == nullptr) { + return false; + } + + return usd_reader->topology_changed(existing_mesh, time); +} + +void USD_CacheReader_incref(CacheReader *reader) +{ + USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader); + usd_reader->incref(); +} + +CacheReader *CacheReader_open_usd_object(CacheArchiveHandle *handle, + CacheReader *reader, + Object *object, + const char *object_path) +{ + if (object_path[0] == '\0') { + return reader; + } + + USDStageReader *archive = stage_reader_from_handle(handle); + + if (!archive || !archive->valid()) { + return reader; + } + + pxr::UsdPrim prim = archive->stage()->GetPrimAtPath(pxr::SdfPath(object_path)); + + if (reader) { + USD_CacheReader_free(reader); + } + + /* TODO(makowalski): The handle does not have the proper import params or settings. */ + USDPrimReader *usd_reader = archive->create_reader(prim); + + if (usd_reader == nullptr) { + /* This object is not supported. */ + return nullptr; + } + usd_reader->object(object); + usd_reader->incref(); + + return reinterpret_cast<CacheReader *>(usd_reader); +} + +void USD_CacheReader_free(CacheReader *reader) +{ + USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader); + usd_reader->decref(); + + if (usd_reader->refcount() == 0) { + delete usd_reader; + } +} + +CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, + const char *filename, + ListBase *object_paths) +{ + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename); + + if (!stage) { + return nullptr; + } + + USDImportParams params{}; + + blender::io::usd::ImportSettings settings{}; + convert_to_z_up(stage, &settings); + + USDStageReader *stage_reader = new USDStageReader(stage, params, settings); + + if (object_paths) { + gather_objects_paths(stage->GetPseudoRoot(), object_paths); + } + + return handle_from_stage_reader(stage_reader); +} + +void USD_free_handle(CacheArchiveHandle *handle) +{ + USDStageReader *stage_reader = stage_reader_from_handle(handle); + delete stage_reader; +} + +void USD_get_transform(struct CacheReader *reader, + float r_mat_world[4][4], + float time, + float scale) +{ + if (!reader) { + return; + } + USDXformReader *usd_reader = reinterpret_cast<USDXformReader *>(reader); + + bool is_constant = false; + + /* Convert from the local matrix we obtain from USD to world coordinates + * for Blender. This conversion is done here rather than by Blender due to + * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in + * BKE_constraint_mat_convertspace(). */ + Object *object = usd_reader->object(); + if (object->parent == nullptr) { + /* No parent, so local space is the same as world space. */ + usd_reader->read_matrix(r_mat_world, time, scale, &is_constant); + return; + } + + float mat_parent[4][4]; + BKE_object_get_parent_matrix(object, object->parent, mat_parent); + + float mat_local[4][4]; + usd_reader->read_matrix(mat_local, time, scale, &is_constant); + mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv); + mul_m4_m4m4(r_mat_world, r_mat_world, mat_local); +} diff --git a/source/blender/io/usd/intern/usd_common.cc b/source/blender/io/usd/intern/usd_common.cc new file mode 100644 index 00000000000..0cd9c3019ef --- /dev/null +++ b/source/blender/io/usd/intern/usd_common.cc @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#include "usd_common.h" + +#include <pxr/base/plug/registry.h> + +#include "BKE_appdir.h" + +namespace blender::io::usd { + +void ensure_usd_plugin_path_registered() +{ + static bool plugin_path_registered = false; + if (plugin_path_registered) { + return; + } + plugin_path_registered = true; + + /* Tell USD which directory to search for its JSON files. If 'datafiles/usd' + * does not exist, the USD library will not be able to read or write any files. */ + const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd"); + /* The trailing slash indicates to the USD library that the path is a directory. */ + pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/"); +} + +} // namespace blender::io::usd diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/io/usd/intern/usd_common.h index d1a4bd3ad7a..36667bbc6b1 100644 --- a/source/blender/nodes/NOD_type_callbacks.hh +++ b/source/blender/io/usd/intern/usd_common.h @@ -12,25 +12,14 @@ * 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. */ - #pragma once -#include <optional> - -#include "BKE_node.h" - -#include "FN_multi_function_data_type.hh" - -namespace blender::nodes { - -using fn::CPPType; -using fn::MFDataType; +namespace blender::io::usd { -const CPPType *socket_cpp_type_get(const bNodeSocketType &stype); -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype); -bool socket_is_mf_data_socket(const bNodeSocketType &stype); -bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value); -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder); +void ensure_usd_plugin_path_registered(); -} // namespace blender::nodes +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc new file mode 100644 index 00000000000..2732ed5770d --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_camera.cc @@ -0,0 +1,100 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_camera.h" + +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_object.h" + +#include "BLI_math.h" + +#include <pxr/pxr.h> +#include <pxr/usd/usdGeom/camera.h> + +namespace blender::io::usd { + +void USDCameraReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, name_.c_str())); + + object_ = BKE_object_add_only_object(bmain, OB_CAMERA, name_.c_str()); + object_->data = bcam; +} + +void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Camera *bcam = (Camera *)object_->data; + + pxr::UsdGeomCamera cam_prim(prim_); + + if (!cam_prim) { + return; + } + + pxr::VtValue val; + cam_prim.GetFocalLengthAttr().Get(&val, motionSampleTime); + pxr::VtValue verApOffset; + cam_prim.GetVerticalApertureOffsetAttr().Get(&verApOffset, motionSampleTime); + pxr::VtValue horApOffset; + cam_prim.GetHorizontalApertureOffsetAttr().Get(&horApOffset, motionSampleTime); + pxr::VtValue clippingRangeVal; + cam_prim.GetClippingRangeAttr().Get(&clippingRangeVal, motionSampleTime); + pxr::VtValue focalDistanceVal; + cam_prim.GetFocusDistanceAttr().Get(&focalDistanceVal, motionSampleTime); + pxr::VtValue fstopVal; + cam_prim.GetFStopAttr().Get(&fstopVal, motionSampleTime); + pxr::VtValue projectionVal; + cam_prim.GetProjectionAttr().Get(&projectionVal, motionSampleTime); + pxr::VtValue verAp; + cam_prim.GetVerticalApertureAttr().Get(&verAp, motionSampleTime); + pxr::VtValue horAp; + cam_prim.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime); + + bcam->lens = val.Get<float>(); + /* TODO(makowalski) */ +#if 0 + bcam->sensor_x = 0.0f; + bcam->sensor_y = 0.0f; +#endif + bcam->shiftx = verApOffset.Get<float>(); + bcam->shifty = horApOffset.Get<float>(); + + bcam->type = (projectionVal.Get<pxr::TfToken>().GetString() == "perspective") ? CAM_PERSP : + CAM_ORTHO; + + /* Calling UncheckedGet() to silence compiler warnings. */ + bcam->clip_start = max_ff(0.1f, clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[0]); + bcam->clip_end = clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[1]; + + bcam->dof.focus_distance = focalDistanceVal.Get<float>(); + bcam->dof.aperture_fstop = static_cast<float>(fstopVal.Get<float>()); + + if (bcam->type == CAM_ORTHO) { + bcam->ortho_scale = max_ff(verAp.Get<float>(), horAp.Get<float>()); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_camera.h b/source/blender/io/usd/intern/usd_reader_camera.h new file mode 100644 index 00000000000..a4156aa8be2 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_camera.h @@ -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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +namespace blender::io::usd { + +class USDCameraReader : public USDXformReader { + + public: + USDCameraReader(const pxr::UsdPrim &object, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(object, import_params, settings) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc new file mode 100644 index 00000000000..31ecf27cf7e --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -0,0 +1,256 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation, + * Copyright (C) 2016 Kévin Dietrich. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_curve.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include <pxr/base/vt/array.h> +#include <pxr/base/vt/types.h> +#include <pxr/base/vt/value.h> + +#include <pxr/usd/usdGeom/basisCurves.h> +#include <pxr/usd/usdGeom/curves.h> + +namespace blender::io::usd { + +void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + + curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->actvert = CU_ACT_NONE; + curve_->resolu = 2; + + object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_->data = curve_; +} + +void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime) +{ + Curve *cu = (Curve *)object_->data; + read_curve_sample(cu, motionSampleTime); + + if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + add_cache_modifier(); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime) +{ + curve_prim_ = pxr::UsdGeomBasisCurves(prim_); + + if (!curve_prim_) { + return; + } + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + pxr::VtFloatArray usdWidths; + widthsAttr.Get(&usdWidths, motionSampleTime); + + pxr::UsdAttribute basisAttr = curve_prim_.GetBasisAttr(); + pxr::TfToken basis; + basisAttr.Get(&basis, motionSampleTime); + + pxr::UsdAttribute typeAttr = curve_prim_.GetTypeAttr(); + pxr::TfToken type; + typeAttr.Get(&type, motionSampleTime); + + pxr::UsdAttribute wrapAttr = curve_prim_.GetWrapAttr(); + pxr::TfToken wrap; + wrapAttr.Get(&wrap, motionSampleTime); + + pxr::VtVec3fArray usdNormals; + curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime); + + /* If normals, extrude, else bevel. + * Perhaps to be replaced by Blender/USD Schema. */ + if (!usdNormals.empty()) { + /* Set extrusion to 1.0f. */ + curve_->ext1 = 1.0f; + } + else { + /* Set bevel depth to 1.0f. */ + curve_->ext2 = 1.0f; + } + + size_t idx = 0; + for (size_t i = 0; i < num_subcurves; i++) { + const int num_verts = usdCounts[i]; + Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__)); + + if (basis == pxr::UsdGeomTokens->bspline) { + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + } + else if (basis == pxr::UsdGeomTokens->bezier) { + /* TODO(makowalski): Beziers are not properly imported as beziers. */ + nu->type = CU_POLY; + } + else if (basis.IsEmpty()) { + nu->type = CU_POLY; + } + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + nu->pntsu = num_verts; + nu->pntsv = 1; + + if (type == pxr::UsdGeomTokens->cubic) { + nu->orderu = 4; + } + else if (type == pxr::UsdGeomTokens->linear) { + nu->orderu = 2; + } + + if (wrap == pxr::UsdGeomTokens->periodic) { + nu->flagu |= CU_NURB_CYCLIC; + } + else if (wrap == pxr::UsdGeomTokens->pinned) { + nu->flagu |= CU_NURB_ENDPOINT; + } + + float weight = 1.0f; + + nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__)); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { + bp->vec[0] = (float)usdPoints[idx][0]; + bp->vec[1] = (float)usdPoints[idx][1]; + bp->vec[2] = (float)usdPoints[idx][2]; + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->weight = weight; + + float radius = curve_->width; + if (idx < usdWidths.size()) { + radius = usdWidths[idx]; + } + + bp->radius = radius; + } + + BKE_nurb_knot_calc_u(nu); + BKE_nurb_knot_calc_v(nu); + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} + +Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh, + const double motionSampleTime, + const int /* read_flag */, + const char ** /* err_str */) +{ + if (!curve_prim_) { + return existing_mesh; + } + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + int vertex_idx = 0; + int curve_idx; + Curve *curve = static_cast<Curve *>(object_->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + bool same_topology = curve_count == num_subcurves; + + if (same_topology) { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int num_in_usd = usdCounts[curve_idx]; + const int num_in_blender = nurbs->pntsu; + + if (num_in_usd != num_in_blender) { + same_topology = false; + break; + } + } + } + + if (!same_topology) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, motionSampleTime); + } + else { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int totpoint = usdCounts[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { + point->vec[0] = usdPoints[vertex_idx][0]; + point->vec[1] = usdPoints[vertex_idx][1]; + point->vec[2] = usdPoints[vertex_idx][2]; + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { + bezier->vec[1][0] = usdPoints[vertex_idx][0]; + bezier->vec[1][1] = usdPoints[vertex_idx][1]; + bezier->vec[1][2] = usdPoints[vertex_idx][2]; + } + } + } + } + + return BKE_mesh_new_nomain_from_curve(object_); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_curve.h b/source/blender/io/usd/intern/usd_reader_curve.h new file mode 100644 index 00000000000..1e676bbbd02 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_curve.h @@ -0,0 +1,62 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation, + * Copyright (C) 2016 Kévin Dietrich. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/basisCurves.h" + +struct Curve; + +namespace blender::io::usd { + +class USDCurvesReader : public USDGeomReader { + protected: + pxr::UsdGeomBasisCurves curve_prim_; + Curve *curve_; + + public: + USDCurvesReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr) + { + } + + bool valid() const override + { + return static_cast<bool>(curve_prim_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_curve_sample(Curve *cu, double motionSampleTime); + + Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_geom.cc b/source/blender/io/usd/intern/usd_reader_geom.cc new file mode 100644 index 00000000000..23c5f57120c --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_geom.cc @@ -0,0 +1,59 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_geom.h" + +#include "BKE_lib_id.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_cachefile_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +namespace blender::io::usd { + +void USDGeomReader::add_cache_modifier() +{ + ModifierData *md = BKE_modifier_new(eModifierType_MeshSequenceCache); + BLI_addtail(&object_->modifiers, md); + + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); + + mcmd->cache_file = settings_->cache_file; + id_us_plus(&mcmd->cache_file->id); + mcmd->read_flag = import_params_.mesh_read_flag; + + BLI_strncpy(mcmd->object_path, prim_.GetPath().GetString().c_str(), FILE_MAX); +} + +void USDGeomReader::add_subdiv_modifier() +{ + ModifierData *md = BKE_modifier_new(eModifierType_Subsurf); + BLI_addtail(&object_->modifiers, md); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h new file mode 100644 index 00000000000..99e22248f2b --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_geom.h @@ -0,0 +1,52 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +struct Mesh; + +namespace blender::io::usd { + +class USDGeomReader : public USDXformReader { + + public: + USDGeomReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) + { + } + + virtual Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) = 0; + + virtual bool topology_changed(Mesh * /* existing_mesh */, double /* motionSampleTime */) + { + return true; + } + + void add_cache_modifier(); + void add_subdiv_modifier(); +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_instance.cc b/source/blender/io/usd/intern/usd_reader_instance.cc new file mode 100644 index 00000000000..e645b0237b9 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_instance.cc @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#include "usd_reader_instance.h" + +#include "BKE_object.h" +#include "DNA_object_types.h" + +#include <iostream> + +namespace blender::io::usd { + +USDInstanceReader::USDInstanceReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) +{ +} + +bool USDInstanceReader::valid() const +{ + return prim_.IsValid() && prim_.IsInstance(); +} + +void USDInstanceReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + this->object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); + this->object_->data = nullptr; + this->object_->transflag |= OB_DUPLICOLLECTION; +} + +void USDInstanceReader::set_instance_collection(Collection *coll) +{ + if (this->object_) { + this->object_->instance_collection = coll; + } +} + +pxr::SdfPath USDInstanceReader::proto_path() const +{ + if (pxr::UsdPrim master = prim_.GetMaster()) { + return master.GetPath(); + } + + return pxr::SdfPath(); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_instance.h b/source/blender/io/usd/intern/usd_reader_instance.h new file mode 100644 index 00000000000..efc1c69a7dd --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_instance.h @@ -0,0 +1,47 @@ +/* + * 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. + */ +#pragma once + +#include "usd_reader_xform.h" + +#include <pxr/usd/usdGeom/xform.h> + +struct Collection; + +namespace blender::io::usd { + +/* Wraps the UsdGeomXform schema. Creates a Blender Empty object. */ + +class USDInstanceReader : public USDXformReader { + + public: + USDInstanceReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + + bool valid() const override; + + void create_object(Main *bmain, double motionSampleTime) override; + + void set_instance_collection(Collection *coll); + + pxr::SdfPath proto_path() const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc new file mode 100644 index 00000000000..fda0c17968a --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_light.cc @@ -0,0 +1,252 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_light.h" + +#include "BKE_light.h" +#include "BKE_object.h" + +#include "DNA_light_types.h" +#include "DNA_object_types.h" + +#include <pxr/usd/usdLux/light.h> + +#include <pxr/usd/usdLux/diskLight.h> +#include <pxr/usd/usdLux/distantLight.h> +#include <pxr/usd/usdLux/rectLight.h> +#include <pxr/usd/usdLux/shapingAPI.h> +#include <pxr/usd/usdLux/sphereLight.h> + +#include <iostream> + +namespace blender::io::usd { + +void USDLightReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Light *blight = static_cast<Light *>(BKE_light_add(bmain, name_.c_str())); + + object_ = BKE_object_add_only_object(bmain, OB_LAMP, name_.c_str()); + object_->data = blight; +} + +void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Light *blight = (Light *)object_->data; + + if (blight == nullptr) { + return; + } + + if (!prim_) { + return; + } + + pxr::UsdLuxLight light_prim(prim_); + + if (!light_prim) { + return; + } + + pxr::UsdLuxShapingAPI shaping_api(light_prim); + + /* Set light type. */ + + if (prim_.IsA<pxr::UsdLuxDiskLight>()) { + blight->type = LA_AREA; + blight->area_shape = LA_AREA_DISK; + /* Ellipse lights are not currently supported */ + } + else if (prim_.IsA<pxr::UsdLuxRectLight>()) { + blight->type = LA_AREA; + blight->area_shape = LA_AREA_RECT; + } + else if (prim_.IsA<pxr::UsdLuxSphereLight>()) { + blight->type = LA_LOCAL; + + if (shaping_api && shaping_api.GetShapingConeAngleAttr().IsAuthored()) { + blight->type = LA_SPOT; + } + } + else if (prim_.IsA<pxr::UsdLuxDistantLight>()) { + blight->type = LA_SUN; + } + + /* Set light values. */ + + if (pxr::UsdAttribute intensity_attr = light_prim.GetIntensityAttr()) { + float intensity = 0.0f; + if (intensity_attr.Get(&intensity, motionSampleTime)) { + blight->energy = intensity * this->import_params_.light_intensity_scale; + } + } + + /* TODO(makowalsk): Not currently supported. */ +#if 0 + pxr::VtValue exposure; + light_prim.GetExposureAttr().Get(&exposure, motionSampleTime); +#endif + + /* TODO(makowalsk): Not currently supported */ +#if 0 + pxr::VtValue diffuse; + light_prim.GetDiffuseAttr().Get(&diffuse, motionSampleTime); +#endif + + if (pxr::UsdAttribute spec_attr = light_prim.GetSpecularAttr()) { + float spec = 0.0f; + if (spec_attr.Get(&spec, motionSampleTime)) { + blight->spec_fac = spec; + } + } + + if (pxr::UsdAttribute color_attr = light_prim.GetColorAttr()) { + pxr::GfVec3f color; + if (color_attr.Get(&color, motionSampleTime)) { + blight->r = color[0]; + blight->g = color[1]; + blight->b = color[2]; + } + } + + /* TODO(makowalski): Not currently supported. */ +#if 0 + pxr::VtValue use_color_temp; + light_prim.GetEnableColorTemperatureAttr().Get(&use_color_temp, motionSampleTime); +#endif + + /* TODO(makowalski): Not currently supported. */ +#if 0 + pxr::VtValue color_temp; + light_prim.GetColorTemperatureAttr().Get(&color_temp, motionSampleTime); +#endif + + switch (blight->type) { + case LA_AREA: + if (blight->area_shape == LA_AREA_RECT && prim_.IsA<pxr::UsdLuxRectLight>()) { + + pxr::UsdLuxRectLight rect_light(prim_); + + if (!rect_light) { + break; + } + + if (pxr::UsdAttribute width_attr = rect_light.GetWidthAttr()) { + float width = 0.0f; + if (width_attr.Get(&width, motionSampleTime)) { + blight->area_size = width; + } + } + + if (pxr::UsdAttribute height_attr = rect_light.GetHeightAttr()) { + float height = 0.0f; + if (height_attr.Get(&height, motionSampleTime)) { + blight->area_sizey = height; + } + } + } + else if (blight->area_shape == LA_AREA_DISK && prim_.IsA<pxr::UsdLuxDiskLight>()) { + + pxr::UsdLuxDiskLight disk_light(prim_); + + if (!disk_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = disk_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius * 2.0f; + } + } + } + break; + case LA_LOCAL: + if (prim_.IsA<pxr::UsdLuxSphereLight>()) { + + pxr::UsdLuxSphereLight sphere_light(prim_); + + if (!sphere_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius; + } + } + } + break; + case LA_SPOT: + if (prim_.IsA<pxr::UsdLuxSphereLight>()) { + + pxr::UsdLuxSphereLight sphere_light(prim_); + + if (!sphere_light) { + break; + } + + if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { + float radius = 0.0f; + if (radius_attr.Get(&radius, motionSampleTime)) { + blight->area_size = radius; + } + } + + if (!shaping_api) { + break; + } + + if (pxr::UsdAttribute cone_angle_attr = shaping_api.GetShapingConeAngleAttr()) { + float cone_angle = 0.0f; + if (cone_angle_attr.Get(&cone_angle, motionSampleTime)) { + blight->spotsize = cone_angle * ((float)M_PI / 180.0f) * 2.0f; + } + } + + if (pxr::UsdAttribute cone_softness_attr = shaping_api.GetShapingConeSoftnessAttr()) { + float cone_softness = 0.0f; + if (cone_softness_attr.Get(&cone_softness, motionSampleTime)) { + blight->spotblend = cone_softness; + } + } + } + break; + case LA_SUN: + if (prim_.IsA<pxr::UsdLuxDistantLight>()) { + pxr::UsdLuxDistantLight distant_light(prim_); + + if (!distant_light) { + break; + } + + if (pxr::UsdAttribute angle_attr = distant_light.GetAngleAttr()) { + float angle = 0.0f; + if (angle_attr.Get(&angle, motionSampleTime)) { + blight->sun_angle = angle * (float)M_PI / 180.0f; + } + } + } + break; + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_light.h b/source/blender/io/usd/intern/usd_reader_light.h new file mode 100644 index 00000000000..e7860fd2c80 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_light.h @@ -0,0 +1,41 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +namespace blender::io::usd { + +class USDLightReader : public USDXformReader { + + public: + USDLightReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc new file mode 100644 index 00000000000..02ed7c35e57 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -0,0 +1,703 @@ +/* + * 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 NVIDIA Corporation. + * All rights reserved. + */ + +#include "usd_reader_material.h" + +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_node.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "DNA_material_types.h" + +#include <pxr/base/gf/vec3f.h> +#include <pxr/usd/usdShade/material.h> +#include <pxr/usd/usdShade/shader.h> + +#include <iostream> +#include <vector> + +namespace usdtokens { + +/* Parameter names. */ +static const pxr::TfToken a("a", pxr::TfToken::Immortal); +static const pxr::TfToken b("b", pxr::TfToken::Immortal); +static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal); +static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal); +static const pxr::TfToken diffuseColor("diffuseColor", pxr::TfToken::Immortal); +static const pxr::TfToken emissiveColor("emissiveColor", pxr::TfToken::Immortal); +static const pxr::TfToken file("file", pxr::TfToken::Immortal); +static const pxr::TfToken g("g", pxr::TfToken::Immortal); +static const pxr::TfToken ior("ior", pxr::TfToken::Immortal); +static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal); +static const pxr::TfToken normal("normal", pxr::TfToken::Immortal); +static const pxr::TfToken occlusion("occlusion", pxr::TfToken::Immortal); +static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal); +static const pxr::TfToken opacityThreshold("opacityThreshold", pxr::TfToken::Immortal); +static const pxr::TfToken r("r", pxr::TfToken::Immortal); +static const pxr::TfToken result("result", pxr::TfToken::Immortal); +static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal); +static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal); +static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); +static const pxr::TfToken sourceColorSpace("sourceColorSpace", pxr::TfToken::Immortal); +static const pxr::TfToken specularColor("specularColor", pxr::TfToken::Immortal); +static const pxr::TfToken st("st", pxr::TfToken::Immortal); +static const pxr::TfToken varname("varname", pxr::TfToken::Immortal); + +/* Color space names. */ +static const pxr::TfToken raw("raw", pxr::TfToken::Immortal); +static const pxr::TfToken RAW("RAW", pxr::TfToken::Immortal); + +/* USD shader names. */ +static const pxr::TfToken UsdPreviewSurface("UsdPreviewSurface", pxr::TfToken::Immortal); +static const pxr::TfToken UsdPrimvarReader_float2("UsdPrimvarReader_float2", + pxr::TfToken::Immortal); +static const pxr::TfToken UsdUVTexture("UsdUVTexture", pxr::TfToken::Immortal); +} // namespace usdtokens + +/* Add a node of the given type at the given location coordinates. */ +static bNode *add_node( + const bContext *C, bNodeTree *ntree, const int type, const float locx, const float locy) +{ + bNode *new_node = nodeAddStaticNode(C, ntree, type); + + if (new_node) { + new_node->locx = locx; + new_node->locy = locy; + } + + return new_node; +} + +/* Connect the output socket of node 'source' to the input socket of node 'dest'. */ +static void link_nodes( + bNodeTree *ntree, bNode *source, const char *sock_out, bNode *dest, const char *sock_in) +{ + bNodeSocket *source_socket = nodeFindSocket(source, SOCK_OUT, sock_out); + + if (!source_socket) { + std::cerr << "PROGRAMMER ERROR: Couldn't find output socket " << sock_out << std::endl; + return; + } + + bNodeSocket *dest_socket = nodeFindSocket(dest, SOCK_IN, sock_in); + + if (!dest_socket) { + std::cerr << "PROGRAMMER ERROR: Couldn't find input socket " << sock_in << std::endl; + return; + } + + nodeAddLink(ntree, source, source_socket, dest, dest_socket); +} + +/* Returns true if the given shader may have opacity < 1.0, based + * on heuristics. */ +static bool needs_blend(const pxr::UsdShadeShader &usd_shader) +{ + if (!usd_shader) { + return false; + } + + bool needs_blend = false; + + if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) { + + if (opacity_input.HasConnectedSource()) { + needs_blend = true; + } + else { + pxr::VtValue val; + if (opacity_input.GetAttr().HasAuthoredValue() && opacity_input.GetAttr().Get(&val)) { + float opacity = val.Get<float>(); + needs_blend = opacity < 1.0f; + } + } + } + + return needs_blend; +} + +/* Returns the given shader's opacityThreshold input value, if this input has an + * authored value. Otherwise, returns the given default value. */ +static float get_opacity_threshold(const pxr::UsdShadeShader &usd_shader, + float default_value = 0.0f) +{ + if (!usd_shader) { + return default_value; + } + + pxr::UsdShadeInput opacity_threshold_input = usd_shader.GetInput(usdtokens::opacityThreshold); + + if (!opacity_threshold_input) { + return default_value; + } + + pxr::VtValue val; + if (opacity_threshold_input.GetAttr().HasAuthoredValue() && + opacity_threshold_input.GetAttr().Get(&val)) { + return val.Get<float>(); + } + + return default_value; +} + +static pxr::TfToken get_source_color_space(const pxr::UsdShadeShader &usd_shader) +{ + if (!usd_shader) { + return pxr::TfToken(); + } + + pxr::UsdShadeInput color_space_input = usd_shader.GetInput(usdtokens::sourceColorSpace); + + if (!color_space_input) { + return pxr::TfToken(); + } + + pxr::VtValue color_space_val; + if (color_space_input.Get(&color_space_val) && color_space_val.IsHolding<pxr::TfToken>()) { + return color_space_val.Get<pxr::TfToken>(); + } + + return pxr::TfToken(); +} + +/* Attempts to return in r_preview_surface the UsdPreviewSurface shader source + * of the given material. Returns true if a UsdPreviewSurface source was found + * and returns false otherwise. */ +static bool get_usd_preview_surface(const pxr::UsdShadeMaterial &usd_material, + pxr::UsdShadeShader &r_preview_surface) +{ + if (!usd_material) { + return false; + } + + if (pxr::UsdShadeShader surf_shader = usd_material.ComputeSurfaceSource()) { + /* Check if we have a UsdPreviewSurface shader. */ + pxr::TfToken shader_id; + if (surf_shader.GetShaderId(&shader_id) && shader_id == usdtokens::UsdPreviewSurface) { + r_preview_surface = surf_shader; + return true; + } + } + + return false; +} + +/* Set the Blender material's viewport display color, metallic and roughness + * properties from the given USD preview surface shader's inputs. */ +static void set_viewport_material_props(Material *mtl, const pxr::UsdShadeShader &usd_preview) +{ + if (!(mtl && usd_preview)) { + return; + } + + if (pxr::UsdShadeInput diffuse_color_input = usd_preview.GetInput(usdtokens::diffuseColor)) { + pxr::VtValue val; + if (diffuse_color_input.GetAttr().HasAuthoredValue() && + diffuse_color_input.GetAttr().Get(&val) && val.IsHolding<pxr::GfVec3f>()) { + pxr::GfVec3f color = val.UncheckedGet<pxr::GfVec3f>(); + mtl->r = color[0]; + mtl->g = color[1]; + mtl->b = color[2]; + } + } + + if (pxr::UsdShadeInput metallic_input = usd_preview.GetInput(usdtokens::metallic)) { + pxr::VtValue val; + if (metallic_input.GetAttr().HasAuthoredValue() && metallic_input.GetAttr().Get(&val) && + val.IsHolding<float>()) { + mtl->metallic = val.Get<float>(); + } + } + + if (pxr::UsdShadeInput roughness_input = usd_preview.GetInput(usdtokens::roughness)) { + pxr::VtValue val; + if (roughness_input.GetAttr().HasAuthoredValue() && roughness_input.GetAttr().Get(&val) && + val.IsHolding<float>()) { + mtl->roughness = val.Get<float>(); + } + } +} + +namespace blender::io::usd { + +namespace { + +/* Compute the x- and y-coordinates for placing a new node in an unoccupied region of + * the column with the given index. Returns the coordinates in r_locx and r_locy and + * updates the column-occupancy information in r_ctx. */ +void compute_node_loc(const int column, float *r_locx, float *r_locy, NodePlacementContext *r_ctx) +{ + if (!(r_locx && r_locy && r_ctx)) { + return; + } + + (*r_locx) = r_ctx->origx - column * r_ctx->horizontal_step; + + if (column >= r_ctx->column_offsets.size()) { + r_ctx->column_offsets.push_back(0.0f); + } + + (*r_locy) = r_ctx->origy - r_ctx->column_offsets[column]; + + /* Record the y-offset of the occupied region in + * the column, including padding. */ + r_ctx->column_offsets[column] += r_ctx->vertical_step + 10.0f; +} + +} // End anonymous namespace. + +USDMaterialReader::USDMaterialReader(const USDImportParams ¶ms, Main *bmain) + : params_(params), bmain_(bmain) +{ +} + +Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_material) const +{ + if (!(bmain_ && usd_material)) { + return nullptr; + } + + std::string mtl_name = usd_material.GetPrim().GetName().GetString(); + + /* Create the material. */ + Material *mtl = BKE_material_add(bmain_, mtl_name.c_str()); + + /* Get the UsdPreviewSurface shader source for the material, + * if there is one. */ + pxr::UsdShadeShader usd_preview; + if (get_usd_preview_surface(usd_material, usd_preview)) { + + set_viewport_material_props(mtl, usd_preview); + + /* Optionally, create shader nodes to represent a UsdPreviewSurface. */ + if (params_.import_usd_preview) { + import_usd_preview(mtl, usd_preview); + } + } + + return mtl; +} + +/* Create the Principled BSDF shader node network. */ +void USDMaterialReader::import_usd_preview(Material *mtl, + const pxr::UsdShadeShader &usd_shader) const +{ + if (!(bmain_ && mtl && usd_shader)) { + return; + } + + /* Create the Material's node tree containing the principled BSDF + * and output shaders. */ + + /* Add the node tree. */ + bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree"); + mtl->nodetree = ntree; + mtl->use_nodes = true; + + /* Create the Principled BSDF shader node. */ + bNode *principled = add_node(nullptr, ntree, SH_NODE_BSDF_PRINCIPLED, 0.0f, 300.0f); + + if (!principled) { + std::cerr << "ERROR: Couldn't create SH_NODE_BSDF_PRINCIPLED node for USD shader " + << usd_shader.GetPath() << std::endl; + return; + } + + /* Create the material output node. */ + bNode *output = add_node(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL, 300.0f, 300.0f); + + if (!output) { + std::cerr << "ERROR: Couldn't create SH_NODE_OUTPUT_MATERIAL node for USD shader " + << usd_shader.GetPath() << std::endl; + return; + } + + /* Connect the Principled BSDF node to the output node. */ + link_nodes(ntree, principled, "BSDF", output, "Surface"); + + /* Recursively create the principled shader input networks. */ + set_principled_node_inputs(principled, ntree, usd_shader); + + nodeSetActive(ntree, output); + + ntreeUpdateTree(bmain_, ntree); + + /* Optionally, set the material blend mode. */ + + if (params_.set_material_blend) { + if (needs_blend(usd_shader)) { + float opacity_threshold = get_opacity_threshold(usd_shader, 0.0f); + if (opacity_threshold > 0.0f) { + mtl->blend_method = MA_BM_CLIP; + mtl->alpha_threshold = opacity_threshold; + } + else { + mtl->blend_method = MA_BM_BLEND; + } + } + } +} + +void USDMaterialReader::set_principled_node_inputs(bNode *principled, + bNodeTree *ntree, + const pxr::UsdShadeShader &usd_shader) const +{ + /* The context struct keeps track of the locations for adding + * input nodes. */ + NodePlacementContext context(0.0f, 300.0); + + /* The column index (from right to left relative to the principled + * node) where we're adding the nodes. */ + int column = 0; + + /* Recursively set the principled shader inputs. */ + + if (pxr::UsdShadeInput diffuse_input = usd_shader.GetInput(usdtokens::diffuseColor)) { + set_node_input(diffuse_input, principled, "Base Color", ntree, column, &context); + } + + if (pxr::UsdShadeInput emissive_input = usd_shader.GetInput(usdtokens::emissiveColor)) { + set_node_input(emissive_input, principled, "Emission", ntree, column, &context); + } + + if (pxr::UsdShadeInput specular_input = usd_shader.GetInput(usdtokens::specularColor)) { + set_node_input(specular_input, principled, "Specular", ntree, column, &context); + } + + if (pxr::UsdShadeInput metallic_input = usd_shader.GetInput(usdtokens::metallic)) { + ; + set_node_input(metallic_input, principled, "Metallic", ntree, column, &context); + } + + if (pxr::UsdShadeInput roughness_input = usd_shader.GetInput(usdtokens::roughness)) { + set_node_input(roughness_input, principled, "Roughness", ntree, column, &context); + } + + if (pxr::UsdShadeInput clearcoat_input = usd_shader.GetInput(usdtokens::clearcoat)) { + set_node_input(clearcoat_input, principled, "Clearcoat", ntree, column, &context); + } + + if (pxr::UsdShadeInput clearcoat_roughness_input = usd_shader.GetInput( + usdtokens::clearcoatRoughness)) { + set_node_input( + clearcoat_roughness_input, principled, "Clearcoat Roughness", ntree, column, &context); + } + + if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) { + set_node_input(opacity_input, principled, "Alpha", ntree, column, &context); + } + + if (pxr::UsdShadeInput ior_input = usd_shader.GetInput(usdtokens::ior)) { + set_node_input(ior_input, principled, "IOR", ntree, column, &context); + } + + if (pxr::UsdShadeInput normal_input = usd_shader.GetInput(usdtokens::normal)) { + set_node_input(normal_input, principled, "Normal", ntree, column, &context); + } +} + +/* Convert the given USD shader input to an input on the given Blender node. */ +void USDMaterialReader::set_node_input(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!(usd_input && dest_node && r_ctx)) { + return; + } + + if (usd_input.HasConnectedSource()) { + /* The USD shader input has a connected source shader. Follow the connection + * and attempt to convert the connected USD shader to a Blender node. */ + follow_connection(usd_input, dest_node, dest_socket_name, ntree, column, r_ctx); + } + else { + /* Set the destination node socket value from the USD shader input value. */ + + bNodeSocket *sock = nodeFindSocket(dest_node, SOCK_IN, dest_socket_name); + if (!sock) { + std::cerr << "ERROR: couldn't get destination node socket " << dest_socket_name << std::endl; + return; + } + + pxr::VtValue val; + if (!usd_input.Get(&val)) { + std::cerr << "ERROR: couldn't get value for usd shader input " + << usd_input.GetPrim().GetPath() << std::endl; + return; + } + + switch (sock->type) { + case SOCK_FLOAT: + if (val.IsHolding<float>()) { + ((bNodeSocketValueFloat *)sock->default_value)->value = val.UncheckedGet<float>(); + } + else if (val.IsHolding<pxr::GfVec3f>()) { + pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>(); + float average = (v3f[0] + v3f[1] + v3f[2]) / 3.0f; + ((bNodeSocketValueFloat *)sock->default_value)->value = average; + } + break; + case SOCK_RGBA: + if (val.IsHolding<pxr::GfVec3f>()) { + pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>(); + copy_v3_v3(((bNodeSocketValueRGBA *)sock->default_value)->value, v3f.data()); + } + break; + case SOCK_VECTOR: + if (val.IsHolding<pxr::GfVec3f>()) { + pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>(); + copy_v3_v3(((bNodeSocketValueVector *)sock->default_value)->value, v3f.data()); + } + else if (val.IsHolding<pxr::GfVec2f>()) { + pxr::GfVec2f v2f = val.UncheckedGet<pxr::GfVec2f>(); + copy_v2_v2(((bNodeSocketValueVector *)sock->default_value)->value, v2f.data()); + } + break; + default: + std::cerr << "WARNING: unexpected type " << sock->idname << " for destination node socket " + << dest_socket_name << std::endl; + break; + } + } +} + +/* Follow the connected source of the USD input to create corresponding inputs + * for the given Blender node. */ +void USDMaterialReader::follow_connection(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const +{ + if (!(usd_input && dest_node && dest_socket_name && ntree && r_ctx)) { + return; + } + + pxr::UsdShadeConnectableAPI source; + pxr::TfToken source_name; + pxr::UsdShadeAttributeType source_type; + + usd_input.GetConnectedSource(&source, &source_name, &source_type); + + if (!(source && source.GetPrim().IsA<pxr::UsdShadeShader>())) { + return; + } + + pxr::UsdShadeShader source_shader(source.GetPrim()); + + if (!source_shader) { + return; + } + + pxr::TfToken shader_id; + if (!source_shader.GetShaderId(&shader_id)) { + std::cerr << "ERROR: couldn't get shader id for source shader " + << source_shader.GetPrim().GetPath() << std::endl; + return; + } + + /* For now, only convert UsdUVTexture and UsdPrimvarReader_float2 inputs. */ + if (shader_id == usdtokens::UsdUVTexture) { + + if (strcmp(dest_socket_name, "Normal") == 0) { + + /* The normal texture input requires creating a normal map node. */ + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column + 1, &locx, &locy, r_ctx); + + bNode *normal_map = add_node(nullptr, ntree, SH_NODE_NORMAL_MAP, locx, locy); + + /* Currently, the Normal Map node has Tangent Space as the default, + * which is what we need, so we don't need to explicitly set it. */ + + /* Connect the Normal Map to the Normal input. */ + link_nodes(ntree, normal_map, "Normal", dest_node, "Normal"); + + /* Now, create the Texture Image node input to the Normal Map "Color" input. */ + convert_usd_uv_texture( + source_shader, source_name, normal_map, "Color", ntree, column + 2, r_ctx); + } + else { + convert_usd_uv_texture( + source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx); + } + } + else if (shader_id == usdtokens::UsdPrimvarReader_float2) { + convert_usd_primvar_reader_float2( + source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx); + } +} + +void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) { + return; + } + + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column, &locx, &locy, r_ctx); + + /* Create the Texture Image node. */ + bNode *tex_image = add_node(nullptr, ntree, SH_NODE_TEX_IMAGE, locx, locy); + + if (!tex_image) { + std::cerr << "ERROR: Couldn't create SH_NODE_TEX_IMAGE for node input " << dest_socket_name + << std::endl; + return; + } + + /* Load the texture image. */ + load_tex_image(usd_shader, tex_image); + + /* Connect to destination node input. */ + + /* Get the source socket name. */ + std::string source_socket_name = usd_source_name == usdtokens::a ? "Alpha" : "Color"; + + link_nodes(ntree, tex_image, source_socket_name.c_str(), dest_node, dest_socket_name); + + /* Connect the texture image node "Vector" input. */ + if (pxr::UsdShadeInput st_input = usd_shader.GetInput(usdtokens::st)) { + set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx); + } +} + +/* Load the texture image node's texture from the path given by the USD shader's + * file input value. */ +void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, + bNode *tex_image) const +{ + if (!(usd_shader && tex_image && tex_image->type == SH_NODE_TEX_IMAGE)) { + return; + } + + /* Try to load the texture image. */ + pxr::UsdShadeInput file_input = usd_shader.GetInput(usdtokens::file); + + if (!file_input) { + std::cerr << "WARNING: Couldn't get file input for USD shader " << usd_shader.GetPath() + << std::endl; + return; + } + + pxr::VtValue file_val; + if (!file_input.Get(&file_val) || !file_val.IsHolding<pxr::SdfAssetPath>()) { + std::cerr << "WARNING: Couldn't get file input value for USD shader " << usd_shader.GetPath() + << std::endl; + return; + } + + const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>(); + std::string file_path = asset_path.GetResolvedPath(); + if (file_path.empty()) { + std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path + << "' for Texture Image node." << std::endl; + return; + } + + const char *im_file = file_path.c_str(); + Image *image = BKE_image_load_exists(bmain_, im_file); + if (!image) { + std::cerr << "WARNING: Couldn't open image file '" << im_file << "' for Texture Image node." + << std::endl; + return; + } + + tex_image->id = &image->id; + + /* Set texture color space. + * TODO(makowalski): For now, just checking for RAW color space, + * assuming sRGB otherwise, but more complex logic might be + * required if the color space is "auto". */ + + pxr::TfToken color_space = get_source_color_space(usd_shader); + + if (color_space.IsEmpty()) { + color_space = file_input.GetAttr().GetColorSpace(); + } + + if (color_space == usdtokens::RAW || color_space == usdtokens::raw) { + STRNCPY(image->colorspace_settings.name, "Raw"); + } +} + +/* This function creates a Blender UV Map node, under the simplifying assumption that + * UsdPrimvarReader_float2 shaders output UV coordinates. + * TODO(makowalski): investigate supporting conversion to other Blender node types + * (e.g., Attribute Nodes) if needed. */ +void USDMaterialReader::convert_usd_primvar_reader_float2( + const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken & /* usd_source_name */, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + const int column, + NodePlacementContext *r_ctx) const +{ + if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) { + return; + } + + float locx = 0.0f; + float locy = 0.0f; + compute_node_loc(column, &locx, &locy, r_ctx); + + /* Create the UV Map node. */ + bNode *uv_map = add_node(nullptr, ntree, SH_NODE_UVMAP, locx, locy); + + if (!uv_map) { + std::cerr << "ERROR: Couldn't create SH_NODE_UVMAP for node input " << dest_socket_name + << std::endl; + return; + } + + /* Set the texmap name. */ + pxr::UsdShadeInput varname_input = usd_shader.GetInput(usdtokens::varname); + if (varname_input) { + pxr::VtValue varname_val; + if (varname_input.Get(&varname_val) && varname_val.IsHolding<pxr::TfToken>()) { + std::string varname = varname_val.Get<pxr::TfToken>().GetString(); + if (!varname.empty()) { + NodeShaderUVMap *storage = (NodeShaderUVMap *)uv_map->storage; + BLI_strncpy(storage->uv_map, varname.c_str(), sizeof(storage->uv_map)); + } + } + } + + /* Connect to destination node input. */ + link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h new file mode 100644 index 00000000000..a17504bd590 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -0,0 +1,132 @@ +/* + * 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 NVIDIA Corporation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" + +#include <pxr/usd/usdShade/material.h> + +struct Main; +struct Material; +struct bNode; +struct bNodeTree; + +namespace blender::io::usd { + +/* Helper struct used when arranging nodes in columns, keeping track the + * occupancy information for a given column. I.e., for column n, + * column_offsets[n] is the y-offset (from top to bottom) of the occupied + * region in that column. */ +struct NodePlacementContext { + float origx; + float origy; + std::vector<float> column_offsets; + const float horizontal_step; + const float vertical_step; + + NodePlacementContext(float in_origx, + float in_origy, + float in_horizontal_step = 300.0f, + float in_vertical_step = 300.0f) + : origx(in_origx), + origy(in_origy), + column_offsets(64, 0.0f), + horizontal_step(in_horizontal_step), + vertical_step(in_vertical_step) + { + } +}; + +/* Converts USD materials to Blender representation. */ + +/** + By default, the #USDMaterialReader creates a Blender material with + * the same name as the USD material. If the USD material has a + * #UsdPreviewSurface source, the Blender material's viewport display + * color, roughness and metallic properties are set to the corresponding + * #UsdPreoviewSurface inputs. + * + * If the Import USD Preview option is enabled, the current implementation + * converts #UsdPreviewSurface to Blender nodes as follows: + * + * - #UsdPreviewSurface -> Principled BSDF + * - #UsdUVTexture -> Texture Image + Normal Map + * - UsdPrimvarReader_float2 -> UV Map + * + * Limitations: arbitrary primvar readers or UsdTransform2d not yet + * supported. For #UsdUVTexture, only the file, st and #sourceColorSpace + * inputs are handled. + * + * TODO(makowalski): Investigate adding support for converting additional + * shaders and inputs. Supporting certain types of inputs, such as texture + * scale and bias, will probably require creating Blender Group nodes with + * the corresponding inputs. + */ +class USDMaterialReader { + protected: + USDImportParams params_; + + Main *bmain_; + + public: + USDMaterialReader(const USDImportParams ¶ms, Main *bmain); + + Material *add_material(const pxr::UsdShadeMaterial &usd_material) const; + + protected: + void import_usd_preview(Material *mtl, const pxr::UsdShadeShader &usd_shader) const; + + void set_principled_node_inputs(bNode *principled_node, + bNodeTree *ntree, + const pxr::UsdShadeShader &usd_shader) const; + + void set_node_input(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void follow_connection(const pxr::UsdShadeInput &usd_input, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; + + void load_tex_image(const pxr::UsdShadeShader &usd_shader, bNode *tex_image) const; + + void convert_usd_primvar_reader_float2(const pxr::UsdShadeShader &usd_shader, + const pxr::TfToken &usd_source_name, + bNode *dest_node, + const char *dest_socket_name, + bNodeTree *ntree, + int column, + NodePlacementContext *r_ctx) const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc new file mode 100644 index 00000000000..9c75bc8afae --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -0,0 +1,853 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ + +#include "usd_reader_mesh.h" +#include "usd_reader_material.h" + +#include "BKE_customdata.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_string.h" + +#include "DNA_customdata_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include <pxr/base/vt/array.h> +#include <pxr/base/vt/types.h> +#include <pxr/base/vt/value.h> +#include <pxr/usd/sdf/types.h> +#include <pxr/usd/usdGeom/mesh.h> +#include <pxr/usd/usdGeom/subset.h> +#include <pxr/usd/usdShade/materialBindingAPI.h> + +#include <iostream> + +namespace usdtokens { +/* Materials */ +static const pxr::TfToken st("st", pxr::TfToken::Immortal); +static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal); +static const pxr::TfToken Cd("Cd", pxr::TfToken::Immortal); +static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal); +static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal); +} // namespace usdtokens + +namespace utils { +/* Very similar to #blender::io::alembic::utils. */ +static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map) +{ + if (r_mat_map == nullptr) { + return; + } + + Material *material = static_cast<Material *>(bmain->materials.first); + + for (; material; material = static_cast<Material *>(material->id.next)) { + /* We have to do this because the stored material name is coming directly from USD. */ + (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material; + } +} + +static void assign_materials(Main *bmain, + Object *ob, + const std::map<pxr::SdfPath, int> &mat_index_map, + const USDImportParams ¶ms, + pxr::UsdStageRefPtr stage) +{ + if (!(stage && bmain && ob)) { + return; + } + + bool can_assign = true; + std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin(); + + int matcount = 0; + for (; it != mat_index_map.end(); ++it, matcount++) { + if (!BKE_object_material_slot_add(bmain, ob)) { + can_assign = false; + break; + } + } + + if (!can_assign) { + return; + } + + /* TODO(kevin): use global map? */ + std::map<std::string, Material *> mat_map; + build_mat_map(bmain, &mat_map); + + blender::io::usd::USDMaterialReader mat_reader(params, bmain); + + for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) { + std::string mat_name = it->first.GetName(); + + std::map<std::string, Material *>::iterator mat_iter = mat_map.find(mat_name); + + Material *assigned_mat = nullptr; + + if (mat_iter == mat_map.end()) { + /* Blender material doesn't exist, so create it now. */ + + /* Look up the USD material. */ + pxr::UsdPrim prim = stage->GetPrimAtPath(it->first); + pxr::UsdShadeMaterial usd_mat(prim); + + if (!usd_mat) { + std::cout << "WARNING: Couldn't construct USD material from prim " << it->first + << std::endl; + continue; + } + + /* Add the Blender material. */ + assigned_mat = mat_reader.add_material(usd_mat); + + if (!assigned_mat) { + std::cout << "WARNING: Couldn't create Blender material from USD material " << it->first + << std::endl; + continue; + } + + mat_map[mat_name] = assigned_mat; + } + else { + /* We found an existing Blender material. */ + assigned_mat = mat_iter->second; + } + + if (assigned_mat) { + BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA); + } + else { + /* This shouldn't happen. */ + std::cout << "WARNING: Couldn't assign material " << mat_name << std::endl; + } + } +} + +} // namespace utils + +static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type) +{ + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr; + CustomData *loopdata; + int numloops; + + /* unsupported custom data type -- don't do anything. */ + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + return nullptr; + } + + loopdata = &mesh->ldata; + cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + if (cd_ptr != nullptr) { + /* layer already exists, so just return it. */ + return cd_ptr; + } + + /* Create a new layer. */ + numloops = mesh->totloop; + cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name); + return cd_ptr; +} + +namespace blender::io::usd { + +USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), + mesh_prim_(prim), + is_left_handed_(false), + has_uvs_(false), + is_time_varying_(false), + is_initial_load_(false) +{ +} + +void USDMeshReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Mesh *mesh = BKE_mesh_add(bmain, name_.c_str()); + + object_ = BKE_object_add_only_object(bmain, OB_MESH, name_.c_str()); + object_->data = mesh; +} + +void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Mesh *mesh = (Mesh *)object_->data; + + is_initial_load_ = true; + Mesh *read_mesh = this->read_mesh( + mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr); + + is_initial_load_ = false; + if (read_mesh != mesh) { + /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ + /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ + short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); + mesh->flag |= autosmooth; + } + + readFaceSetsSample(bmain, mesh, motionSampleTime); + + if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + is_time_varying_ = true; + } + + if (is_time_varying_) { + add_cache_modifier(); + } + + if (import_params_.import_subdiv) { + pxr::TfToken subdivScheme; + mesh_prim_.GetSubdivisionSchemeAttr().Get(&subdivScheme, motionSampleTime); + + if (subdivScheme == pxr::UsdGeomTokens->catmullClark) { + add_subdiv_modifier(); + } + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +bool USDMeshReader::valid() const +{ + return static_cast<bool>(mesh_prim_); +} + +bool USDMeshReader::topology_changed(Mesh *existing_mesh, const double motionSampleTime) +{ + /* TODO(makowalski): Is it the best strategy to cache the mesh + * geometry in this function? This needs to be revisited. */ + + mesh_prim_.GetFaceVertexIndicesAttr().Get(&face_indices_, motionSampleTime); + mesh_prim_.GetFaceVertexCountsAttr().Get(&face_counts_, motionSampleTime); + mesh_prim_.GetPointsAttr().Get(&positions_, motionSampleTime); + + /* TODO(makowalski): Reading normals probably doesn't belong in this function, + * as this is not required to determine if the topology has changed. */ + + /* If 'normals' and 'primvars:normals' are both specified, the latter has precedence. */ + pxr::UsdGeomPrimvar primvar = mesh_prim_.GetPrimvar(usdtokens::normalsPrimvar); + if (primvar.HasValue()) { + primvar.ComputeFlattened(&normals_, motionSampleTime); + normal_interpolation_ = primvar.GetInterpolation(); + } + else { + mesh_prim_.GetNormalsAttr().Get(&normals_, motionSampleTime); + normal_interpolation_ = mesh_prim_.GetNormalsInterpolation(); + } + + return positions_.size() != existing_mesh->totvert || + face_counts_.size() != existing_mesh->totpoly || + face_indices_.size() != existing_mesh->totloop; +} + +void USDMeshReader::read_mpolys(Mesh *mesh) +{ + MPoly *mpolys = mesh->mpoly; + MLoop *mloops = mesh->mloop; + + int loop_index = 0; + + for (int i = 0; i < face_counts_.size(); i++) { + const int face_size = face_counts_[i]; + + MPoly &poly = mpolys[i]; + poly.loopstart = loop_index; + poly.totloop = face_size; + poly.mat_nr = 0; + + /* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded, + * this is encoded in custom loop normals. */ + poly.flag |= ME_SMOOTH; + + if (is_left_handed_) { + int loop_end_index = loop_index + (face_size - 1); + for (int f = 0; f < face_size; ++f, ++loop_index) { + mloops[loop_index].v = face_indices_[loop_end_index - f]; + } + } + else { + for (int f = 0; f < face_size; ++f, ++loop_index) { + mloops[loop_index].v = face_indices_[loop_index]; + } + } + } + + BKE_mesh_calc_edges(mesh, false, false); +} + +void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bool load_uvs) +{ + unsigned int loop_index = 0; + unsigned int rev_loop_index = 0; + unsigned int uv_index = 0; + + const CustomData *ldata = &mesh->ldata; + + struct UVSample { + pxr::VtVec2fArray uvs; + pxr::TfToken interpolation; + }; + + std::vector<UVSample> uv_primvars(ldata->totlayer); + + if (has_uvs_) { + for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { + const CustomDataLayer *layer = &ldata->layers[layer_idx]; + std::string layer_name = std::string(layer->name); + if (layer->type != CD_MLOOPUV) { + continue; + } + + pxr::TfToken uv_token; + + /* If first time seeing uv token, store in map of `<layer->uid, TfToken>`. */ + if (uv_token_map_.find(layer_name) == uv_token_map_.end()) { + uv_token = pxr::TfToken(layer_name); + uv_token_map_.insert(std::make_pair(layer_name, uv_token)); + } + else { + uv_token = uv_token_map_.at(layer_name); + } + + /* Early out if no token found, this should never happen */ + if (uv_token.IsEmpty()) { + continue; + } + /* Early out if not first load and UVs aren't animated. */ + if (!load_uvs && primvar_varying_map_.find(uv_token) != primvar_varying_map_.end() && + !primvar_varying_map_.at(uv_token)) { + continue; + } + + /* Early out if mesh doesn't have primvar. */ + if (!mesh_prim_.HasPrimvar(uv_token)) { + continue; + } + + if (pxr::UsdGeomPrimvar uv_primvar = mesh_prim_.GetPrimvar(uv_token)) { + uv_primvar.ComputeFlattened(&uv_primvars[layer_idx].uvs, motionSampleTime); + uv_primvars[layer_idx].interpolation = uv_primvar.GetInterpolation(); + } + } + } + + for (int i = 0; i < face_counts_.size(); i++) { + const int face_size = face_counts_[i]; + + rev_loop_index = loop_index + (face_size - 1); + + for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) { + + for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { + const CustomDataLayer *layer = &ldata->layers[layer_idx]; + if (layer->type != CD_MLOOPUV) { + continue; + } + + /* Early out if mismatched layer sizes. */ + if (layer_idx > uv_primvars.size()) { + continue; + } + + /* Early out if no uvs loaded. */ + if (uv_primvars[layer_idx].uvs.empty()) { + continue; + } + + const UVSample &sample = uv_primvars[layer_idx]; + + if (!(sample.interpolation == pxr::UsdGeomTokens->faceVarying || + sample.interpolation == pxr::UsdGeomTokens->vertex)) { + std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation + << " for uv " << layer->name << std::endl; + continue; + } + + /* For Vertex interpolation, use the vertex index. */ + int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ? + mesh->mloop[loop_index].v : + loop_index; + + if (usd_uv_index >= sample.uvs.size()) { + std::cerr << "WARNING: out of bounds uv index " << usd_uv_index << " for uv " + << layer->name << " of size " << sample.uvs.size() << std::endl; + continue; + } + + MLoopUV *mloopuv = static_cast<MLoopUV *>(layer->data); + if (is_left_handed_) { + uv_index = rev_loop_index; + } + else { + uv_index = loop_index; + } + mloopuv[uv_index].uv[0] = sample.uvs[usd_uv_index][0]; + mloopuv[uv_index].uv[1] = sample.uvs[usd_uv_index][1]; + } + } + } +} + +void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime) +{ + if (!(mesh && mesh_prim_ && mesh->totloop > 0)) { + return; + } + + /* Early out if we read the display color before and if this attribute isn't animated. */ + if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() && + !primvar_varying_map_.at(usdtokens::displayColor)) { + return; + } + + pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar(); + + if (!color_primvar.HasValue()) { + return; + } + + pxr::TfToken interp = color_primvar.GetInterpolation(); + + if (interp == pxr::UsdGeomTokens->varying) { + std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl; + return; + } + + if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) { + bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying(); + primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying)); + if (might_be_time_varying) { + is_time_varying_ = true; + } + } + + pxr::VtArray<pxr::GfVec3f> display_colors; + + if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) { + std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl; + return; + } + + if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) || + (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) || + (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) { + std::cerr << "WARNING: display colors count mismatch\n" << std::endl; + return; + } + + void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_MLOOPCOL); + + if (!cd_ptr) { + std::cerr << "WARNING: Couldn't add displayColors custom data.\n"; + return; + } + + MLoopCol *colors = static_cast<MLoopCol *>(cd_ptr); + + mesh->mloopcol = colors; + + MPoly *poly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) { + for (int j = 0; j < poly->totloop; ++j) { + int loop_index = poly->loopstart + j; + + /* Default for constant varying interpolation. */ + int usd_index = 0; + + if (interp == pxr::UsdGeomTokens->vertex) { + usd_index = mesh->mloop[loop_index].v; + } + else if (interp == pxr::UsdGeomTokens->faceVarying) { + usd_index = poly->loopstart; + if (is_left_handed_) { + usd_index += poly->totloop - 1 - j; + } + else { + usd_index += j; + } + } + else if (interp == pxr::UsdGeomTokens->uniform) { + /* Uniform varying uses the poly index. */ + usd_index = i; + } + + if (usd_index >= display_colors.size()) { + continue; + } + + colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]); + colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]); + colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]); + colors[loop_index].a = unit_float_to_uchar_clamp(1.0); + } + } +} + +void USDMeshReader::process_normals_vertex_varying(Mesh *mesh) +{ + if (!mesh) { + return; + } + + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + if (normals_.size() != mesh->totvert) { + std::cerr << "WARNING: vertex varying normals count mismatch for mesh " << prim_path_ + << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + for (int i = 0; i < normals_.size(); i++) { + MVert &mvert = mesh->mvert[i]; + normal_float_to_short_v3(mvert.no, normals_[i].data()); + } +} + +void USDMeshReader::process_normals_face_varying(Mesh *mesh) +{ + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + /* Check for normals count mismatches to prevent crashes. */ + if (normals_.size() != mesh->totloop) { + std::cerr << "WARNING: loop normal count mismatch for mesh " << mesh->id.name << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + mesh->flag |= ME_AUTOSMOOTH; + + long int loop_count = normals_.size(); + + float(*lnors)[3] = static_cast<float(*)[3]>( + MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals")); + + MPoly *mpoly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) { + for (int j = 0; j < mpoly->totloop; j++) { + int blender_index = mpoly->loopstart + j; + + int usd_index = mpoly->loopstart; + if (is_left_handed_) { + usd_index += mpoly->totloop - 1 - j; + } + else { + usd_index += j; + } + + lnors[blender_index][0] = normals_[usd_index][0]; + lnors[blender_index][1] = normals_[usd_index][1]; + lnors[blender_index][2] = normals_[usd_index][2]; + } + } + BKE_mesh_set_custom_normals(mesh, lnors); + + MEM_freeN(lnors); +} + +/* Set USD uniform (per-face) normals as Blender loop normals. */ +void USDMeshReader::process_normals_uniform(Mesh *mesh) +{ + if (normals_.empty()) { + BKE_mesh_calc_normals(mesh); + return; + } + + /* Check for normals count mismatches to prevent crashes. */ + if (normals_.size() != mesh->totpoly) { + std::cerr << "WARNING: uniform normal count mismatch for mesh " << mesh->id.name << std::endl; + BKE_mesh_calc_normals(mesh); + return; + } + + float(*lnors)[3] = static_cast<float(*)[3]>( + MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals")); + + MPoly *mpoly = mesh->mpoly; + + for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) { + + for (int j = 0; j < mpoly->totloop; j++) { + int loop_index = mpoly->loopstart + j; + lnors[loop_index][0] = normals_[i][0]; + lnors[loop_index][1] = normals_[i][1]; + lnors[loop_index][2] = normals_[i][2]; + } + } + + mesh->flag |= ME_AUTOSMOOTH; + BKE_mesh_set_custom_normals(mesh, lnors); + + MEM_freeN(lnors); +} + +void USDMeshReader::read_mesh_sample(ImportSettings *settings, + Mesh *mesh, + const double motionSampleTime, + const bool new_mesh) +{ + /* Note that for new meshes we always want to read verts and polys, + * regardless of the value of the read_flag, to avoid a crash downstream + * in code that expect this data to be there. */ + + if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + for (int i = 0; i < positions_.size(); i++) { + MVert &mvert = mesh->mvert[i]; + mvert.co[0] = positions_[i][0]; + mvert.co[1] = positions_[i][1]; + mvert.co[2] = positions_[i][2]; + } + } + + if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(mesh); + if (normal_interpolation_ == pxr::UsdGeomTokens->faceVarying) { + process_normals_face_varying(mesh); + } + else if (normal_interpolation_ == pxr::UsdGeomTokens->uniform) { + process_normals_uniform(mesh); + } + else { + /* Default */ + BKE_mesh_calc_normals(mesh); + } + } + + /* Process point normals after reading polys. This + * is important in the case where the normals are empty + * and we invoke BKE_mesh_calc_normals(mesh), which requires + * edges to be defined. */ + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0 && + normal_interpolation_ == pxr::UsdGeomTokens->vertex) { + process_normals_vertex_varying(mesh); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs(mesh, motionSampleTime, new_mesh); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { + read_colors(mesh, motionSampleTime); + } +} + +void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, + MPoly *mpoly, + const int /* totpoly */, + std::map<pxr::SdfPath, int> *r_mat_map) +{ + if (r_mat_map == nullptr) { + return; + } + + /* Find the geom subsets that have bound materials. + * We don't call #pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets() + * because this function returns only those subsets that are in the 'materialBind' + * family, but, in practice, applications (like Houdini) might export subsets + * in different families that are bound to materials. + * TODO(makowalski): Reassess if the above is the best approach. */ + const std::vector<pxr::UsdGeomSubset> subsets = pxr::UsdGeomSubset::GetAllGeomSubsets( + mesh_prim_); + + int current_mat = 0; + if (!subsets.empty()) { + for (const pxr::UsdGeomSubset &subset : subsets) { + pxr::UsdShadeMaterialBindingAPI subset_api = pxr::UsdShadeMaterialBindingAPI( + subset.GetPrim()); + + pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial(); + + if (!subset_mtl) { + continue; + } + + pxr::SdfPath subset_mtl_path = subset_mtl.GetPath(); + + if (subset_mtl_path.IsEmpty()) { + continue; + } + + if (r_mat_map->find(subset_mtl_path) == r_mat_map->end()) { + (*r_mat_map)[subset_mtl_path] = 1 + current_mat++; + } + + const int mat_idx = (*r_mat_map)[subset_mtl_path] - 1; + + pxr::UsdAttribute indicesAttribute = subset.GetIndicesAttr(); + pxr::VtIntArray indices; + indicesAttribute.Get(&indices, motionSampleTime); + + for (int i = 0; i < indices.size(); i++) { + MPoly &poly = mpoly[indices[i]]; + poly.mat_nr = mat_idx; + } + } + } + + if (r_mat_map->empty()) { + pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim_); + + if (pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial()) { + + pxr::SdfPath mtl_path = mtl.GetPath(); + + if (!mtl_path.IsEmpty()) { + r_mat_map->insert(std::make_pair(mtl.GetPath(), 1)); + } + } + } +} + +void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double motionSampleTime) +{ + if (!import_params_.import_materials) { + return; + } + + std::map<pxr::SdfPath, int> mat_map; + assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map); + utils::assign_materials(bmain, object_, mat_map, this->import_params_, this->prim_.GetStage()); +} + +Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, + const double motionSampleTime, + const int read_flag, + const char ** /* err_str */) +{ + if (!mesh_prim_) { + return existing_mesh; + } + + mesh_prim_.GetOrientationAttr().Get(&orientation_); + if (orientation_ == pxr::UsdGeomTokens->leftHanded) { + is_left_handed_ = true; + } + + std::vector<pxr::TfToken> uv_tokens; + + /* Currently we only handle UV primvars. */ + if (read_flag & MOD_MESHSEQ_READ_UV) { + + std::vector<pxr::UsdGeomPrimvar> primvars = mesh_prim_.GetPrimvars(); + + for (pxr::UsdGeomPrimvar p : primvars) { + + pxr::TfToken name = p.GetPrimvarName(); + pxr::SdfValueTypeName type = p.GetTypeName(); + + bool is_uv = false; + + /* Assume all UVs are stored in one of these primvar types */ + if (type == pxr::SdfValueTypeNames->TexCoord2hArray || + type == pxr::SdfValueTypeNames->TexCoord2fArray || + type == pxr::SdfValueTypeNames->TexCoord2dArray) { + is_uv = true; + } + /* In some cases, the st primvar is stored as float2 values. */ + else if (name == usdtokens::st && type == pxr::SdfValueTypeNames->Float2Array) { + is_uv = true; + } + + if (is_uv) { + + pxr::TfToken interp = p.GetInterpolation(); + + if (!(interp == pxr::UsdGeomTokens->faceVarying || interp == pxr::UsdGeomTokens->vertex)) { + continue; + } + + uv_tokens.push_back(p.GetBaseName()); + has_uvs_ = true; + + /* Record whether the UVs might be time varying. */ + if (primvar_varying_map_.find(name) == primvar_varying_map_.end()) { + bool might_be_time_varying = p.ValueMightBeTimeVarying(); + primvar_varying_map_.insert(std::make_pair(name, might_be_time_varying)); + if (might_be_time_varying) { + is_time_varying_ = true; + } + } + } + } + } + + Mesh *active_mesh = existing_mesh; + bool new_mesh = false; + + /* TODO(makowalski): implement the optimization of only updating the mesh points when + * the topology is consistent, as in the Alembic importer. */ + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (topology_changed(existing_mesh, motionSampleTime)) { + new_mesh = true; + active_mesh = BKE_mesh_new_nomain_from_template( + existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size()); + + for (pxr::TfToken token : uv_tokens) { + void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV); + active_mesh->mloopuv = static_cast<MLoopUV *>(cd_ptr); + } + } + + read_mesh_sample(&settings, active_mesh, motionSampleTime, new_mesh || is_initial_load_); + + if (new_mesh) { + /* Here we assume that the number of materials doesn't change, i.e. that + * the material slots that were created when the object was loaded from + * USD are still valid now. */ + size_t num_polys = active_mesh->totpoly; + if (num_polys > 0 && import_params_.import_materials) { + std::map<pxr::SdfPath, int> mat_map; + assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map); + } + } + + return active_mesh; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h new file mode 100644 index 00000000000..54ad144d191 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -0,0 +1,95 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/mesh.h" + +struct MPoly; + +namespace blender::io::usd { + +class USDMeshReader : public USDGeomReader { + private: + pxr::UsdGeomMesh mesh_prim_; + + std::unordered_map<std::string, pxr::TfToken> uv_token_map_; + std::map<const pxr::TfToken, bool> primvar_varying_map_; + + /* TODO(makowalski): Is it the best strategy to cache the + * mesh geometry in the following members? It appears these + * arrays are never cleared, so this might bloat memory. */ + pxr::VtIntArray face_indices_; + pxr::VtIntArray face_counts_; + pxr::VtVec3fArray positions_; + pxr::VtVec3fArray normals_; + + pxr::TfToken normal_interpolation_; + pxr::TfToken orientation_; + bool is_left_handed_; + bool has_uvs_; + bool is_time_varying_; + + /* This is to ensure we load all data once, because we reuse the read_mesh function + * in the mesh seq modifier, and in initial load. Ideally, a better fix would be + * implemented. Note this will break if faces or positions vary. */ + bool is_initial_load_; + + public: + USDMeshReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + + bool valid() const override; + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + struct Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; + + bool topology_changed(Mesh *existing_mesh, double motionSampleTime) override; + + private: + void process_normals_vertex_varying(Mesh *mesh); + void process_normals_face_varying(Mesh *mesh); + void process_normals_uniform(Mesh *mesh); + void readFaceSetsSample(Main *bmain, Mesh *mesh, double motionSampleTime); + void assign_facesets_to_mpoly(double motionSampleTime, + struct MPoly *mpoly, + int totpoly, + std::map<pxr::SdfPath, int> *r_mat_map); + + void read_mpolys(Mesh *mesh); + void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false); + void read_colors(Mesh *mesh, double motionSampleTime); + + void read_mesh_sample(ImportSettings *settings, + Mesh *mesh, + double motionSampleTime, + bool new_mesh); +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc new file mode 100644 index 00000000000..9b30b524729 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -0,0 +1,256 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_nurbs.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "MEM_guardedalloc.h" + +#include <pxr/base/vt/array.h> +#include <pxr/base/vt/types.h> +#include <pxr/base/vt/value.h> +#include <pxr/usd/sdf/types.h> + +#include <pxr/usd/usdGeom/curves.h> + +static bool set_knots(const pxr::VtDoubleArray &knots, float *&nu_knots) +{ + if (knots.empty()) { + return false; + } + + /* Skip first and last knots, as they are used for padding. */ + const size_t num_knots = knots.size(); + nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), __func__)); + + for (size_t i = 0; i < num_knots; i++) { + nu_knots[i] = (float)knots[i]; + } + + return true; +} + +namespace blender::io::usd { + +void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + + curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->actvert = CU_ACT_NONE; + curve_->resolu = 2; + + object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_->data = curve_; +} + +void USDNurbsReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + Curve *cu = (Curve *)object_->data; + read_curve_sample(cu, motionSampleTime); + + if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { + add_cache_modifier(); + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime) +{ + curve_prim_ = pxr::UsdGeomNurbsCurves(prim_); + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + vertexAttr.Get(&usdCounts, motionSampleTime); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + pxr::VtFloatArray usdWidths; + widthsAttr.Get(&usdWidths, motionSampleTime); + + pxr::VtIntArray orders; + curve_prim_.GetOrderAttr().Get(&orders, motionSampleTime); + + pxr::VtDoubleArray knots; + curve_prim_.GetKnotsAttr().Get(&knots, motionSampleTime); + + pxr::VtVec3fArray usdNormals; + curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime); + + /* If normals, extrude, else bevel. + * Perhaps to be replaced by Blender USD Schema. */ + if (!usdNormals.empty()) { + /* Set extrusion to 1. */ + curve_->ext1 = 1.0f; + } + else { + /* Set bevel depth to 1. */ + curve_->ext2 = 1.0f; + } + + size_t idx = 0; + for (size_t i = 0; i < usdCounts.size(); i++) { + const int num_verts = usdCounts[i]; + + Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__)); + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + nu->pntsu = num_verts; + nu->pntsv = 1; + + if (i < orders.size()) { + nu->orderu = static_cast<short>(orders[i]); + } + else { + nu->orderu = 4; + nu->orderv = 4; + } + + /* TODO(makowalski): investigate setting Cyclic U and Endpoint U options. */ +#if 0 + if (knots.size() > 3) { + if ((knots[0] == knots[1]) && (knots[knots.size()] == knots[knots.size() - 1])) { + nu->flagu |= CU_NURB_ENDPOINT; + } else { + nu->flagu |= CU_NURB_CYCLIC; + } + } +#endif + + float weight = 1.0f; + + nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__)); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { + bp->vec[0] = (float)usdPoints[idx][0]; + bp->vec[1] = (float)usdPoints[idx][1]; + bp->vec[2] = (float)usdPoints[idx][2]; + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->weight = weight; + + float radius = 0.1f; + if (idx < usdWidths.size()) { + radius = usdWidths[idx]; + } + + bp->radius = radius; + } + + if (!set_knots(knots, nu->knotsu)) { + BKE_nurb_knot_calc_u(nu); + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} + +Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */, + const double motionSampleTime, + const int /* read_flag */, + const char ** /* err_str */) +{ + pxr::UsdGeomCurves curve_prim_(prim_); + + pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); + pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); + pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); + + pxr::VtIntArray usdCounts; + + vertexAttr.Get(&usdCounts, motionSampleTime); + int num_subcurves = usdCounts.size(); + + pxr::VtVec3fArray usdPoints; + pointsAttr.Get(&usdPoints, motionSampleTime); + + int vertex_idx = 0; + int curve_idx; + Curve *curve = static_cast<Curve *>(object_->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + bool same_topology = curve_count == num_subcurves; + + if (same_topology) { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int num_in_usd = usdCounts[curve_idx]; + const int num_in_blender = nurbs->pntsu; + + if (num_in_usd != num_in_blender) { + same_topology = false; + break; + } + } + } + + if (!same_topology) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, motionSampleTime); + } + else { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int totpoint = usdCounts[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { + point->vec[0] = usdPoints[vertex_idx][0]; + point->vec[1] = usdPoints[vertex_idx][1]; + point->vec[2] = usdPoints[vertex_idx][2]; + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { + bezier->vec[1][0] = usdPoints[vertex_idx][0]; + bezier->vec[1][1] = usdPoints[vertex_idx][1]; + bezier->vec[1][2] = usdPoints[vertex_idx][2]; + } + } + } + } + + return BKE_mesh_new_nomain_from_curve(object_); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.h b/source/blender/io/usd/intern/usd_reader_nurbs.h new file mode 100644 index 00000000000..33a4acf503e --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_nurbs.h @@ -0,0 +1,61 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_geom.h" + +#include "pxr/usd/usdGeom/nurbsCurves.h" + +struct Curve; + +namespace blender::io::usd { + +class USDNurbsReader : public USDGeomReader { + protected: + pxr::UsdGeomNurbsCurves curve_prim_; + Curve *curve_; + + public: + USDNurbsReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr) + { + } + + bool valid() const override + { + return static_cast<bool>(curve_prim_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_curve_sample(Curve *cu, double motionSampleTime); + + Mesh *read_mesh(struct Mesh *existing_mesh, + double motionSampleTime, + int read_flag, + const char **err_str) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.cc b/source/blender/io/usd/intern/usd_reader_prim.cc new file mode 100644 index 00000000000..abd70f49f23 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_prim.cc @@ -0,0 +1,80 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_prim.h" + +#include "BLI_utildefines.h" + +namespace blender::io::usd { + +USDPrimReader::USDPrimReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : name_(prim.GetName().GetString()), + prim_path_(prim.GetPrimPath().GetString()), + object_(nullptr), + prim_(prim), + import_params_(import_params), + parent_reader_(nullptr), + settings_(&settings), + refcount_(0) +{ +} + +USDPrimReader::~USDPrimReader() = default; + +const pxr::UsdPrim &USDPrimReader::prim() const +{ + return prim_; +} + +Object *USDPrimReader::object() const +{ + return object_; +} + +void USDPrimReader::object(Object *ob) +{ + object_ = ob; +} + +bool USDPrimReader::valid() const +{ + return prim_.IsValid(); +} + +int USDPrimReader::refcount() const +{ + return refcount_; +} + +void USDPrimReader::incref() +{ + refcount_++; +} + +void USDPrimReader::decref() +{ + refcount_--; + BLI_assert(refcount_ >= 0); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h new file mode 100644 index 00000000000..5aff52f011f --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -0,0 +1,131 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" + +#include <pxr/usd/usd/prim.h> + +struct Main; +struct Object; + +namespace blender::io::usd { + +struct ImportSettings { + bool do_convert_mat; + float conversion_mat[4][4]; + + int from_up; + int from_forward; + float scale; + bool is_sequence; + bool set_frame_range; + + /* Length and frame offset of file sequences. */ + int sequence_len; + int sequence_offset; + + /* From MeshSeqCacheModifierData.read_flag */ + int read_flag; + + bool validate_meshes; + + CacheFile *cache_file; + + ImportSettings() + : do_convert_mat(false), + from_up(0), + from_forward(0), + scale(1.0f), + is_sequence(false), + set_frame_range(false), + sequence_len(1), + sequence_offset(0), + read_flag(0), + validate_meshes(false), + cache_file(NULL) + { + } +}; + +/* Most generic USD Reader. */ + +class USDPrimReader { + + protected: + std::string name_; + std::string prim_path_; + Object *object_; + pxr::UsdPrim prim_; + const USDImportParams &import_params_; + USDPrimReader *parent_reader_; + const ImportSettings *settings_; + int refcount_; + + public: + USDPrimReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings); + virtual ~USDPrimReader(); + + const pxr::UsdPrim &prim() const; + + virtual bool valid() const; + + virtual void create_object(Main *bmain, double motionSampleTime) = 0; + virtual void read_object_data(Main * /* bmain */, double /* motionSampleTime */){}; + + Object *object() const; + void object(Object *ob); + + USDPrimReader *parent() const + { + return parent_reader_; + } + void parent(USDPrimReader *parent) + { + parent_reader_ = parent; + } + + /* Since readers might be referenced through handles + * maintained by modifiers and constraints, we provide + * a reference count to facilitate managing the object + * lifetime. + * TODO(makowalski): investigate transitioning to using + * smart pointers for readers, or, alternatively look into + * making the lifetime management more robust, e.g., by + * making the destructors protected and implementing deletion + * in decref(), etc. */ + int refcount() const; + void incref(); + void decref(); + + const std::string &name() const + { + return name_; + } + const std::string &prim_path() const + { + return prim_path_; + } +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc new file mode 100644 index 00000000000..233b3d9da4d --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -0,0 +1,324 @@ +/* + * 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 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ + +#include "usd_reader_stage.h" +#include "usd_reader_camera.h" +#include "usd_reader_curve.h" +#include "usd_reader_instance.h" +#include "usd_reader_light.h" +#include "usd_reader_mesh.h" +#include "usd_reader_nurbs.h" +#include "usd_reader_prim.h" +#include "usd_reader_volume.h" +#include "usd_reader_xform.h" + +#include <pxr/pxr.h> +#include <pxr/usd/usdGeom/camera.h> +#include <pxr/usd/usdGeom/curves.h> +#include <pxr/usd/usdGeom/mesh.h> +#include <pxr/usd/usdGeom/nurbsCurves.h> +#include <pxr/usd/usdGeom/scope.h> +#include <pxr/usd/usdLux/light.h> + +#include <iostream> + +namespace blender::io::usd { + +USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage, + const USDImportParams ¶ms, + const ImportSettings &settings) + : stage_(stage), params_(params), settings_(settings) +{ +} + +USDStageReader::~USDStageReader() +{ + clear_readers(); +} + +bool USDStageReader::valid() const +{ + return stage_; +} + +USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim) +{ + if (params_.import_cameras && prim.IsA<pxr::UsdGeomCamera>()) { + return new USDCameraReader(prim, params_, settings_); + } + if (params_.import_curves && prim.IsA<pxr::UsdGeomBasisCurves>()) { + return new USDCurvesReader(prim, params_, settings_); + } + if (params_.import_curves && prim.IsA<pxr::UsdGeomNurbsCurves>()) { + return new USDNurbsReader(prim, params_, settings_); + } + if (params_.import_meshes && prim.IsA<pxr::UsdGeomMesh>()) { + return new USDMeshReader(prim, params_, settings_); + } + if (params_.import_lights && prim.IsA<pxr::UsdLuxLight>()) { + return new USDLightReader(prim, params_, settings_); + } + if (params_.import_volumes && prim.IsA<pxr::UsdVolVolume>()) { + return new USDVolumeReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdGeomImageable>()) { + return new USDXformReader(prim, params_, settings_); + } + + return nullptr; +} + +USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim) +{ + if (prim.IsA<pxr::UsdGeomCamera>()) { + return new USDCameraReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdGeomBasisCurves>()) { + return new USDCurvesReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdGeomNurbsCurves>()) { + return new USDNurbsReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdGeomMesh>()) { + return new USDMeshReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdLuxLight>()) { + return new USDLightReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdVolVolume>()) { + return new USDVolumeReader(prim, params_, settings_); + } + if (prim.IsA<pxr::UsdGeomImageable>()) { + return new USDXformReader(prim, params_, settings_); + } + return nullptr; +} + +/* Returns true if the given prim should be included in the + * traversal based on the import options and the prim's visibility + * attribute. Note that the prim will be trivially included + * if it has no visibility attribute or if the visibility + * is inherited. */ +bool USDStageReader::include_by_visibility(const pxr::UsdGeomImageable &imageable) const +{ + if (!params_.import_visible_only) { + /* Invisible prims are allowed. */ + return true; + } + + pxr::UsdAttribute visibility_attr = imageable.GetVisibilityAttr(); + + if (!visibility_attr) { + /* No visibility attribute, so allow. */ + return true; + } + + /* Include if the prim has an animating visibility attribute or is not invisible. */ + + if (visibility_attr.ValueMightBeTimeVarying()) { + return true; + } + + pxr::TfToken visibility; + visibility_attr.Get(&visibility); + return visibility != pxr::UsdGeomTokens->invisible; +} + +/* Returns true if the given prim should be included in the + * traversal based on the import options and the prim's purpose + * attribute. E.g., return false (to exclude the prim) if the prim + * represents guide geometry and the 'Import Guide' option is + * toggled off. */ +bool USDStageReader::include_by_purpose(const pxr::UsdGeomImageable &imageable) const +{ + if (params_.import_guide && params_.import_proxy && params_.import_render) { + /* The options allow any purpose, so we trivially include the prim. */ + return true; + } + + pxr::UsdAttribute purpose_attr = imageable.GetPurposeAttr(); + + if (!purpose_attr) { + /* No purpose attribute, so trivially include the prim. */ + return true; + } + + pxr::TfToken purpose; + purpose_attr.Get(&purpose); + + if (purpose == pxr::UsdGeomTokens->guide) { + return params_.import_guide; + } + if (purpose == pxr::UsdGeomTokens->proxy) { + return params_.import_proxy; + } + if (purpose == pxr::UsdGeomTokens->render) { + return params_.import_render; + } + + return true; +} + +/* Determine if the given reader can use the parent of the encapsulated USD prim + * to compute the Blender object's transform. If so, the reader is appropriately + * flagged and the function returns true. Otherwise, the function returns false. */ +static bool merge_with_parent(USDPrimReader *reader) +{ + USDXformReader *xform_reader = dynamic_cast<USDXformReader *>(reader); + + if (!xform_reader) { + return false; + } + + /* Check if the Xform reader is already merged. */ + if (xform_reader->use_parent_xform()) { + return false; + } + + /* Only merge if the parent is an Xform. */ + if (!xform_reader->prim().GetParent().IsA<pxr::UsdGeomXform>()) { + return false; + } + + /* Don't merge Xform and Scope prims. */ + if (xform_reader->prim().IsA<pxr::UsdGeomXform>() || + xform_reader->prim().IsA<pxr::UsdGeomScope>()) { + return false; + } + + /* Don't merge if the prim has authored transform ops. */ + if (xform_reader->prim_has_xform_ops()) { + return false; + } + + /* Flag the Xform reader as merged. */ + xform_reader->set_use_parent_xform(true); + + return true; +} + +USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &prim) +{ + if (prim.IsA<pxr::UsdGeomImageable>()) { + pxr::UsdGeomImageable imageable(prim); + + if (!include_by_purpose(imageable)) { + return nullptr; + } + + if (!include_by_visibility(imageable)) { + return nullptr; + } + } + + pxr::Usd_PrimFlagsPredicate filter_predicate = pxr::UsdPrimDefaultPredicate; + + if (params_.import_instance_proxies) { + filter_predicate = pxr::UsdTraverseInstanceProxies(filter_predicate); + } + + pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); + + std::vector<USDPrimReader *> child_readers; + + for (const auto &childPrim : children) { + if (USDPrimReader *child_reader = collect_readers(bmain, childPrim)) { + child_readers.push_back(child_reader); + } + } + + if (prim.IsPseudoRoot()) { + return nullptr; + } + + /* Check if we can merge an Xform with its child prim. */ + if (child_readers.size() == 1) { + + USDPrimReader *child_reader = child_readers.front(); + + if (merge_with_parent(child_reader)) { + return child_reader; + } + } + + USDPrimReader *reader = create_reader_if_allowed(prim); + + if (!reader) { + return nullptr; + } + + reader->create_object(bmain, 0.0); + + readers_.push_back(reader); + reader->incref(); + + /* Set each child reader's parent. */ + for (USDPrimReader *child_reader : child_readers) { + child_reader->parent(reader); + } + + return reader; +} + +void USDStageReader::collect_readers(Main *bmain) +{ + if (!valid()) { + return; + } + + clear_readers(); + + /* Iterate through the stage. */ + pxr::UsdPrim root = stage_->GetPseudoRoot(); + + std::string prim_path_mask(params_.prim_path_mask); + + if (!prim_path_mask.empty()) { + pxr::UsdPrim prim = stage_->GetPrimAtPath(pxr::SdfPath(prim_path_mask)); + if (prim.IsValid()) { + root = prim; + } + else { + std::cerr << "WARNING: Prim Path Mask " << prim_path_mask + << " does not specify a valid prim.\n"; + } + } + + stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); + collect_readers(bmain, root); +} + +void USDStageReader::clear_readers() +{ + for (USDPrimReader *reader : readers_) { + if (!reader) { + continue; + } + + reader->decref(); + + if (reader->refcount() == 0) { + delete reader; + } + } + + readers_.clear(); +} + +} // Namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h new file mode 100644 index 00000000000..ba223962c0c --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -0,0 +1,90 @@ +/* + * 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 Tangent Animation and + * NVIDIA Corporation. All rights reserved. + */ +#pragma once + +struct Main; + +#include "usd.h" +#include "usd_reader_prim.h" + +#include <pxr/usd/usd/stage.h> +#include <pxr/usd/usdGeom/imageable.h> + +#include <vector> + +struct ImportSettings; + +namespace blender::io::usd { + +typedef std::map<pxr::SdfPath, std::vector<USDPrimReader *>> ProtoReaderMap; + +class USDStageReader { + + protected: + pxr::UsdStageRefPtr stage_; + USDImportParams params_; + ImportSettings settings_; + + std::vector<USDPrimReader *> readers_; + + public: + USDStageReader(pxr::UsdStageRefPtr stage, + const USDImportParams ¶ms, + const ImportSettings &settings); + + ~USDStageReader(); + + USDPrimReader *create_reader_if_allowed(const pxr::UsdPrim &prim); + + USDPrimReader *create_reader(const pxr::UsdPrim &prim); + + void collect_readers(struct Main *bmain); + + bool valid() const; + + pxr::UsdStageRefPtr stage() + { + return stage_; + } + const USDImportParams ¶ms() const + { + return params_; + } + + const ImportSettings &settings() const + { + return settings_; + } + + void clear_readers(); + + const std::vector<USDPrimReader *> &readers() const + { + return readers_; + }; + + private: + USDPrimReader *collect_readers(Main *bmain, const pxr::UsdPrim &prim); + + bool include_by_visibility(const pxr::UsdGeomImageable &imageable) const; + + bool include_by_purpose(const pxr::UsdGeomImageable &imageable) const; +}; + +}; // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_volume.cc b/source/blender/io/usd/intern/usd_reader_volume.cc new file mode 100644 index 00000000000..871f791c1dd --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_volume.cc @@ -0,0 +1,114 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_volume.h" + +#include "BKE_object.h" +#include "BKE_volume.h" + +#include "DNA_object_types.h" +#include "DNA_volume_types.h" + +#include <pxr/usd/usdVol/openVDBAsset.h> +#include <pxr/usd/usdVol/volume.h> + +#include <iostream> + +namespace usdtokens { + +static const pxr::TfToken density("density", pxr::TfToken::Immortal); + +} + +namespace blender::io::usd { + +void USDVolumeReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + Volume *volume = (Volume *)BKE_volume_add(bmain, name_.c_str()); + + object_ = BKE_object_add_only_object(bmain, OB_VOLUME, name_.c_str()); + object_->data = volume; +} + +void USDVolumeReader::read_object_data(Main *bmain, const double motionSampleTime) +{ + if (!volume_) { + return; + } + + Volume *volume = static_cast<Volume *>(object_->data); + + if (!volume) { + return; + } + + pxr::UsdVolVolume::FieldMap fields = volume_.GetFieldPaths(); + + for (pxr::UsdVolVolume::FieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) { + + pxr::UsdPrim fieldPrim = prim_.GetStage()->GetPrimAtPath(it->second); + + if (!fieldPrim.IsA<pxr::UsdVolOpenVDBAsset>()) { + continue; + } + + pxr::UsdVolOpenVDBAsset fieldBase(fieldPrim); + + pxr::UsdAttribute fieldNameAttr = fieldBase.GetFieldNameAttr(); + + if (fieldNameAttr.IsAuthored()) { + pxr::TfToken fieldName; + fieldNameAttr.Get(&fieldName, motionSampleTime); + + /* A Blender volume creates density by default. */ + if (fieldName != usdtokens::density) { + BKE_volume_grid_add(volume, fieldName.GetString().c_str(), VOLUME_GRID_FLOAT); + } + } + + pxr::UsdAttribute filepathAttr = fieldBase.GetFilePathAttr(); + + if (filepathAttr.IsAuthored()) { + pxr::SdfAssetPath fp; + filepathAttr.Get(&fp, motionSampleTime); + + if (filepathAttr.ValueMightBeTimeVarying()) { + std::vector<double> filePathTimes; + filepathAttr.GetTimeSamples(&filePathTimes); + + if (!filePathTimes.empty()) { + int start = static_cast<int>(filePathTimes.front()); + int end = static_cast<int>(filePathTimes.back()); + + volume->is_sequence = static_cast<char>(true); + volume->frame_start = start; + volume->frame_duration = (end - start) + 1; + } + } + + std::string filepath = fp.GetResolvedPath(); + + strcpy(volume->filepath, filepath.c_str()); + } + } + + USDXformReader::read_object_data(bmain, motionSampleTime); +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_volume.h b/source/blender/io/usd/intern/usd_reader_volume.h new file mode 100644 index 00000000000..ca2fddb5531 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_volume.h @@ -0,0 +1,49 @@ +/* + * 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 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_xform.h" + +#include "pxr/usd/usdVol/volume.h" + +namespace blender::io::usd { + +class USDVolumeReader : public USDXformReader { + private: + pxr::UsdVolVolume volume_; + + public: + USDVolumeReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDXformReader(prim, import_params, settings), volume_(prim) + { + } + + bool valid() const override + { + return static_cast<bool>(volume_); + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_xform.cc b/source/blender/io/usd/intern/usd_reader_xform.cc new file mode 100644 index 00000000000..eebcc5eb3d5 --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_xform.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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ + +#include "usd_reader_xform.h" + +#include "BKE_constraint.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_math_geom.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include <pxr/base/gf/math.h> +#include <pxr/base/gf/matrix4f.h> + +#include <pxr/usd/usdGeom/xform.h> + +namespace blender::io::usd { + +void USDXformReader::create_object(Main *bmain, const double /* motionSampleTime */) +{ + object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); + object_->empty_drawsize = 0.1f; + object_->data = nullptr; +} + +void USDXformReader::read_object_data(Main * /* bmain */, const double motionSampleTime) +{ + bool is_constant; + float transform_from_usd[4][4]; + + read_matrix(transform_from_usd, motionSampleTime, import_params_.scale, &is_constant); + + if (!is_constant) { + bConstraint *con = BKE_constraint_add_for_object( + object_, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); + + std::string prim_path = use_parent_xform_ ? prim_.GetParent().GetPath().GetAsString() : + prim_path_; + + BLI_strncpy(data->object_path, prim_path.c_str(), FILE_MAX); + + data->cache_file = settings_->cache_file; + id_us_plus(&data->cache_file->id); + } + + BKE_object_apply_mat4(object_, transform_from_usd, true, false); +} + +void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */, + const float time, + const float scale, + bool *r_is_constant) +{ + if (r_is_constant) { + *r_is_constant = true; + } + + unit_m4(r_mat); + + pxr::UsdGeomXformable xformable; + + if (use_parent_xform_) { + xformable = pxr::UsdGeomXformable(prim_.GetParent()); + } + else { + xformable = pxr::UsdGeomXformable(prim_); + } + + if (!xformable) { + /* This might happen if the prim is a Scope. */ + return; + } + + if (r_is_constant) { + *r_is_constant = !xformable.TransformMightBeTimeVarying(); + } + + pxr::GfMatrix4d usd_local_xf; + bool reset_xform_stack; + xformable.GetLocalTransformation(&usd_local_xf, &reset_xform_stack, time); + + /* Convert the result to a float matrix. */ + pxr::GfMatrix4f mat4f = pxr::GfMatrix4f(usd_local_xf); + mat4f.Get(r_mat); + + /* Apply global scaling and rotation only to root objects, parenting + * will propagate it. */ + if ((scale != 1.0 || settings_->do_convert_mat) && is_root_xform_) { + + if (scale != 1.0f) { + float scale_mat[4][4]; + scale_m4_fl(scale_mat, scale); + mul_m4_m4m4(r_mat, scale_mat, r_mat); + } + + if (settings_->do_convert_mat) { + mul_m4_m4m4(r_mat, settings_->conversion_mat, r_mat); + } + } +} + +bool USDXformReader::prim_has_xform_ops() const +{ + pxr::UsdGeomXformable xformable(prim_); + + if (!xformable) { + /* This might happen if the prim is a Scope. */ + return false; + } + + bool reset_xform_stack = false; + + return !xformable.GetOrderedXformOps(&reset_xform_stack).empty(); +} + +bool USDXformReader::is_root_xform_prim() const +{ + if (!prim_.IsValid()) { + return false; + } + + if (prim_.IsInMaster()) { + /* We don't consider prototypes to be root prims, + * because we never want to apply global scaling + * or rotations to the prototypes themselves. */ + return false; + } + + if (prim_.IsA<pxr::UsdGeomXformable>()) { + /* If this prim doesn't have an ancestor that's a + * UsdGeomXformable, then it's a root prim. Note + * that it's not sufficient to only check the immediate + * parent prim, since the immediate parent could be a + * UsdGeomScope that has an xformable ancestor. */ + pxr::UsdPrim cur_parent = prim_.GetParent(); + + if (use_parent_xform_) { + cur_parent = cur_parent.GetParent(); + } + + while (cur_parent && !cur_parent.IsPseudoRoot()) { + if (cur_parent.IsA<pxr::UsdGeomXformable>()) { + return false; + } + cur_parent = cur_parent.GetParent(); + } + + /* We didn't find an xformable ancestor. */ + return true; + } + + return false; +} + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_xform.h b/source/blender/io/usd/intern/usd_reader_xform.h new file mode 100644 index 00000000000..587ac373a4f --- /dev/null +++ b/source/blender/io/usd/intern/usd_reader_xform.h @@ -0,0 +1,68 @@ +/* + * 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. + * + * Adapted from the Blender Alembic importer implementation. + * + * Modifications Copyright (C) 2021 Tangent Animation. + * All rights reserved. + */ +#pragma once + +#include "usd.h" +#include "usd_reader_prim.h" + +namespace blender::io::usd { + +class USDXformReader : public USDPrimReader { + private: + bool use_parent_xform_; + + /* Indicates if the created object is the root of a + * transform hierarchy. */ + bool is_root_xform_; + + public: + USDXformReader(const pxr::UsdPrim &prim, + const USDImportParams &import_params, + const ImportSettings &settings) + : USDPrimReader(prim, import_params, settings), + use_parent_xform_(false), + is_root_xform_(is_root_xform_prim()) + { + } + + void create_object(Main *bmain, double motionSampleTime) override; + void read_object_data(Main *bmain, double motionSampleTime) override; + + void read_matrix(float r_mat[4][4], const float time, const float scale, bool *r_is_constant); + + bool use_parent_xform() const + { + return use_parent_xform_; + } + void set_use_parent_xform(bool flag) + { + use_parent_xform_ = flag; + is_root_xform_ = is_root_xform_prim(); + } + + bool prim_has_xform_ops() const; + + protected: + /* Returns true if the contained USD prim is the root of a transform hierarchy. */ + bool is_root_xform_prim() const; +}; + +} // namespace blender::io::usd diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 40e2d0d8674..6b6b2d37162 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -26,6 +26,10 @@ extern "C" { #endif struct bContext; +struct Object; +struct CacheArchiveHandle; +struct CacheReader; +struct CacheFile; struct USDExportParams { bool export_animation; @@ -39,6 +43,34 @@ struct USDExportParams { enum eEvaluationMode evaluation_mode; }; +struct USDImportParams { + float scale; + bool is_sequence; + bool set_frame_range; + int sequence_len; + int offset; + bool validate_meshes; + char mesh_read_flag; + bool import_cameras; + bool import_curves; + bool import_lights; + bool import_materials; + bool import_meshes; + bool import_volumes; + char *prim_path_mask; + bool import_subdiv; + bool import_instance_proxies; + bool create_collection; + bool import_guide; + bool import_proxy; + bool import_render; + bool import_visible_only; + bool use_instancing; + bool import_usd_preview; + bool set_material_blend; + float light_intensity_scale; +}; + /* The USD_export takes a as_background_job parameter, and returns a boolean. * * When as_background_job=true, returns false immediately after scheduling @@ -53,8 +85,45 @@ bool USD_export(struct bContext *C, const struct USDExportParams *params, bool as_background_job); +bool USD_import(struct bContext *C, + const char *filepath, + const struct USDImportParams *params, + bool as_background_job); + int USD_get_version(void); +/* USD Import and Mesh Cache interface. */ + +struct CacheArchiveHandle *USD_create_handle(struct Main *bmain, + const char *filename, + struct ListBase *object_paths); + +void USD_free_handle(struct CacheArchiveHandle *handle); + +void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); + +/* Either modifies current_mesh in-place or constructs a new mesh. */ +struct Mesh *USD_read_mesh(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str, + int read_flag); + +bool USD_mesh_topology_changed(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str); + +struct CacheReader *CacheReader_open_usd_object(struct CacheArchiveHandle *handle, + struct CacheReader *reader, + struct Object *object, + const char *object_path); + +void USD_CacheReader_incref(struct CacheReader *reader); +void USD_CacheReader_free(struct CacheReader *reader); + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c9d652ad03d..10a5a0f1c47 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -429,7 +429,8 @@ typedef struct PreviewImage { * BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */ #define ID_IS_OVERRIDABLE_LIBRARY(_id) \ (ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0 && \ - (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) + (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 && \ + !ELEM(GS(((ID *)(_id))->name), ID_SCE)) /* NOTE: The three checks below do not take into account whether given ID is linked or not (when * chaining overrides over several libraries). User must ensure the ID is not linked itself @@ -550,7 +551,7 @@ enum { /* tag data-block as having actually increased user-count for the extra virtual user. */ LIB_TAG_EXTRAUSER_SET = 1 << 7, - /* RESET_AFTER_USE tag newly duplicated/copied IDs. + /* RESET_AFTER_USE tag newly duplicated/copied IDs (see #ID_NEW_SET macro above). * Also used internally in readfile.c to mark data-blocks needing do_versions. */ LIB_TAG_NEW = 1 << 8, /* RESET_BEFORE_USE free test flag. @@ -562,13 +563,32 @@ enum { /** * The data-block is a copy-on-write/localized version. * + * RESET_NEVER + * * \warning This should not be cleared on existing data. * If support for this is needed, see T88026 as this flag controls memory ownership * of physics *shared* pointers. */ LIB_TAG_COPIED_ON_WRITE = 1 << 12, - + /** + * The data-block is not the original COW ID created by the depsgraph, but has be re-allocated + * during the evaluation process of another ID. + * + * RESET_NEVER + * + * Typical example is object data, when evaluating the object's modifier stack the final obdata + * can be different than the COW initial obdata ID. + */ LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13, + + /** + * The data-block is fully outside of any ID management area, and should be considered as a + * purely independent data. + * + * RESET_NEVER + * + * NOTE: Only used by node-groups currently. + */ LIB_TAG_LOCALIZED = 1 << 14, /* RESET_NEVER tag data-block for freeing etc. behavior diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index ca16e6728dd..2975915eccd 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -81,7 +81,7 @@ typedef enum eAssetLibraryType { // ASSET_LIBRARY_PROJECT = 2, /** Display assets from custom asset libraries, as defined in the preferences - * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname + * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library_ref.idname * then. * In RNA, we add the index of the custom library to this to identify it by index. So keep * this last! */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 986c009ac26..634ebdff253 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -76,7 +76,9 @@ typedef struct BrushGpencilSettings { float fill_threshold; /** Number of pixel to consider the leak is too small (x 2). */ short fill_leak; - char _pad2[2]; + /* Type of caps: eGPDstroke_Caps. */ + int8_t caps_type; + char _pad; int flag2; diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h index d37994bb488..74fbe5012ab 100644 --- a/source/blender/makesdna/DNA_cachefile_defaults.h +++ b/source/blender/makesdna/DNA_cachefile_defaults.h @@ -36,9 +36,12 @@ .scale = 1.0f, \ .object_paths ={NULL, NULL}, \ \ + .type = 0, \ .handle = NULL, \ .handle_filepath[0] = '\0', \ .handle_readers = NULL, \ + .use_prefetch = 1, \ + .prefetch_cache_size = 4096, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index 04c99c6c4b1..0f4c53a6e7e 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -31,6 +31,13 @@ extern "C" { struct GSet; +/* CacheFile::type */ +typedef enum { + CACHEFILE_TYPE_ALEMBIC = 1, + CACHEFILE_TYPE_USD = 2, + CACHE_FILE_TYPE_INVALID = 0, +} eCacheFileType; + /* CacheFile::flag */ enum { CACHEFILE_DS_EXPAND = (1 << 0), @@ -44,13 +51,13 @@ enum { }; #endif -/* Representation of an object's path inside the Alembic file. +/* Representation of an object's path inside the archive. * Note that this is not a file path. */ -typedef struct AlembicObjectPath { - struct AlembicObjectPath *next, *prev; +typedef struct CacheObjectPath { + struct CacheObjectPath *next, *prev; char path[4096]; -} AlembicObjectPath; +} CacheObjectPath; /* CacheFile::velocity_unit * Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */ @@ -63,7 +70,7 @@ typedef struct CacheFile { ID id; struct AnimData *adt; - /** Paths of the objects inside of the Alembic archive referenced by this CacheFile. */ + /** Paths of the objects inside of the archive referenced by this CacheFile. */ ListBase object_paths; /** 1024 = FILE_MAX. */ @@ -80,18 +87,36 @@ typedef struct CacheFile { /** The frame offset to subtract. */ float frame_offset; + char _pad[4]; + /** Animation flag. */ short flag; - short draw_flag; /* UNUSED */ - char _pad[3]; + /* eCacheFileType enum. */ + char type; + + /** Do not load data from the cache file and display objects in the scene as boxes, Cycles will + * load objects directly from the CacheFile. Other render engines which can load Alembic data + * directly can take care of rendering it themselves. + */ + char use_render_procedural; + + char _pad1[3]; + + /** Enable data prefetching when using the Cycles Procedural. */ + char use_prefetch; + + /** Size in megabytes for the prefetch cache used by the Cycles Procedural. */ + int prefetch_cache_size; + + char _pad2[7]; char velocity_unit; - /* Name of the velocity property in the Alembic file. */ + /* Name of the velocity property in the archive. */ char velocity_name[64]; /* Runtime */ - struct AbcArchiveHandle *handle; + struct CacheArchiveHandle *handle; char handle_filepath[1024]; struct GSet *handle_readers; } CacheFile; diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h index 1defa8b782b..3eba02de2a3 100644 --- a/source/blender/makesdna/DNA_collection_types.h +++ b/source/blender/makesdna/DNA_collection_types.h @@ -102,10 +102,10 @@ typedef struct Collection { /* Collection->flag */ enum { - COLLECTION_RESTRICT_VIEWPORT = (1 << 0), /* Disable in viewports. */ - COLLECTION_RESTRICT_SELECT = (1 << 1), /* Not selectable in viewport. */ + COLLECTION_HIDE_VIEWPORT = (1 << 0), /* Disable in viewports. */ + COLLECTION_HIDE_SELECT = (1 << 1), /* Not selectable in viewport. */ /* COLLECTION_DISABLED_DEPRECATED = (1 << 2), */ /* Not used anymore */ - COLLECTION_RESTRICT_RENDER = (1 << 3), /* Disable in renders. */ + COLLECTION_HIDE_RENDER = (1 << 3), /* Disable in renders. */ COLLECTION_HAS_OBJECT_CACHE = (1 << 4), /* Runtime: object_cache is populated. */ COLLECTION_IS_MASTER = (1 << 5), /* Is master collection embedded in the scene. */ COLLECTION_HAS_OBJECT_CACHE_INSTANCED = (1 << 6), /* for object_cache_instanced. */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index a77fbc9e45e..822b8705c9b 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -901,10 +901,16 @@ typedef enum eActionConstraint_Flags { typedef enum eActionConstraint_MixMode { /* Multiply the action transformation on the right. */ ACTCON_MIX_AFTER_FULL = 0, + /* Multiply the action transformation on the left. */ + ACTCON_MIX_BEFORE_FULL = 3, /* Multiply the action transformation on the right, with anti-shear scale handling. */ ACTCON_MIX_AFTER = 1, /* Multiply the action transformation on the left, with anti-shear scale handling. */ ACTCON_MIX_BEFORE = 2, + /* Separately combine Translation, Rotation and Scale, with rotation on the right. */ + ACTCON_MIX_AFTER_SPLIT = 4, + /* Separately combine Translation, Rotation and Scale, with rotation on the left. */ + ACTCON_MIX_BEFORE_SPLIT = 5, } eActionConstraint_MixMode; /* Locked-Axis Values (Locked Track) */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 380d8ad1249..68bd2961f23 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -324,6 +324,7 @@ typedef struct bGPDstroke { struct bGPDcurve *editcurve; bGPDstroke_Runtime runtime; + void *_pad5; } bGPDstroke; /** #bGPDstroke.flag */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 828c6ff2a51..63e4597150c 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -222,7 +222,7 @@ enum { enum { LAYER_COLLECTION_HAS_OBJECTS = (1 << 0), /* LAYER_COLLECTION_VISIBLE_DEPSGRAPH = (1 << 1), */ /* UNUSED */ - LAYER_COLLECTION_RESTRICT_VIEWPORT = (1 << 2), + LAYER_COLLECTION_HIDE_VIEWPORT = (1 << 2), LAYER_COLLECTION_VISIBLE_VIEW_LAYER = (1 << 4), }; diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index e93cf050e18..cdb09c3af50 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * 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 @@ -17,12 +15,6 @@ * * The Original Code is Copyright (C) 2010 Blender Foundation. * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL LICENSE BLOCK ***** */ #pragma once diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h index e6a7c004078..419118a38f4 100644 --- a/source/blender/makesdna/DNA_mask_types.h +++ b/source/blender/makesdna/DNA_mask_types.h @@ -174,7 +174,7 @@ typedef struct MaskLayer { /** For animation. */ char flag; /** Matching 'Object' flag of the same name - eventually use in the outliner. */ - char restrictflag; + char visibility_flag; } MaskLayer; /* MaskParent->flag */ @@ -206,10 +206,10 @@ enum { MASK_SPLINE_OFFSET_SMOOTH = 1, }; -/* ob->restrictflag */ -#define MASK_RESTRICT_VIEW (1 << 0) -#define MASK_RESTRICT_SELECT (1 << 1) -#define MASK_RESTRICT_RENDER (1 << 2) +/* MaskLayer->visibility_flag */ +#define MASK_HIDE_VIEW (1 << 0) +#define MASK_HIDE_SELECT (1 << 1) +#define MASK_HIDE_RENDER (1 << 2) /* SpaceClip->mask_draw_flag */ #define MASK_DRAWFLAG_SMOOTH (1 << 0) diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 932f4715298..97f14b2195d 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -231,6 +231,7 @@ typedef struct Mesh { * default and Face Sets can be used without affecting the color of the mesh. */ int face_sets_color_default; + void *_pad2; Mesh_Runtime runtime; } Mesh; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index f66de378c35..1bebbc35747 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -136,6 +136,7 @@ typedef struct ModifierData { /* Runtime field which contains runtime data which is specific to a modifier type. */ void *runtime; + void *_pad1; } ModifierData; typedef enum { @@ -215,6 +216,7 @@ typedef struct LatticeModifierData { float strength; short flag; char _pad[2]; + void *_pad1; } LatticeModifierData; /* Lattice modifier flags. */ @@ -232,6 +234,7 @@ typedef struct CurveModifierData { short defaxis; short flag; char _pad[4]; + void *_pad1; } CurveModifierData; /* Curve modifier flags */ @@ -283,6 +286,7 @@ typedef struct MaskModifierData { /** Flags for various things. */ short flag; float threshold; + void *_pad1; } MaskModifierData; /* Mask Modifier -> mode */ @@ -373,6 +377,7 @@ typedef struct MirrorModifierData { float uv_offset[2]; float uv_offset_copy[2]; struct Object *mirror_ob; + void *_pad1; } MirrorModifierData; /* MirrorModifierData->flag */ @@ -451,6 +456,7 @@ typedef struct BevelModifierData { /** Curve info for the custom profile */ struct CurveProfile *custom_profile; + void *_pad2; } BevelModifierData; /* BevelModifierData->flags and BevelModifierData->lim_flags */ @@ -535,6 +541,7 @@ typedef struct FluidModifierData { float time; /** Domain, inflow, outflow, .... */ int type; + void *_pad1; } FluidModifierData; /* Fluid modifier flags */ @@ -680,6 +687,7 @@ typedef struct CastModifierData { /** MAX_VGROUP_NAME. */ char defgrp_name[64]; short flag, type; + void *_pad1; } CastModifierData; /* Cast modifier flags */ @@ -725,6 +733,7 @@ typedef struct WaveModifierData { float timeoffs, lifetime; char _pad1[4]; + void *_pad2; } WaveModifierData; /* WaveModifierData.flag */ @@ -797,6 +806,7 @@ typedef struct HookModifierData { float force; /** Optional vertexgroup name, MAX_VGROUP_NAME. */ char name[64]; + void *_pad1; } HookModifierData; typedef struct SoftbodyModifierData { @@ -1001,6 +1011,7 @@ typedef struct ParticleSystemModifierData { int totdmvert, totdmedge, totdmface; short flag; char _pad[2]; + void *_pad1; } ParticleSystemModifierData; typedef enum { @@ -1037,6 +1048,7 @@ typedef struct ParticleInstanceModifierData { char index_layer_name[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char value_layer_name[64]; + void *_pad1; } ParticleInstanceModifierData; typedef enum { @@ -1057,6 +1069,7 @@ typedef struct ExplodeModifierData { float protect; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvname[64]; + void *_pad1; } ExplodeModifierData; typedef struct MultiresModifierData { @@ -1086,6 +1099,7 @@ typedef struct FluidsimModifierData { /** Definition is in DNA_object_fluidsim_types.h. */ struct FluidsimSettings *fss; + void *_pad1; } FluidsimModifierData; /* DEPRECATED, only used for versioning. */ @@ -1202,6 +1216,7 @@ typedef struct SimpleDeformModifierData { char deform_axis; char flag; + void *_pad1; } SimpleDeformModifierData; /* SimpleDeform->flag */ @@ -1310,6 +1325,7 @@ typedef struct ScrewModifierData { short flag; char axis; char _pad[5]; + void *_pad1; } ScrewModifierData; enum { @@ -1434,6 +1450,7 @@ typedef struct WarpModifierData { char flag; char falloff_type; char _pad[6]; + void *_pad1; } WarpModifierData; /* WarpModifierData->flag */ @@ -1497,6 +1514,7 @@ typedef struct WeightVGEditModifierData { /* Padding... */ char _pad0[4]; + void *_pad1; } WeightVGEditModifierData; /* WeightVGEdit flags. */ @@ -2064,6 +2082,7 @@ typedef struct DataTransferModifierData { char defgrp_name[64]; int flags; + void *_pad2; } DataTransferModifierData; /* DataTransferModifierData.flags */ @@ -2094,6 +2113,7 @@ typedef struct NormalEditModifierData { float mix_limit; float offset[3]; char _pad0[4]; + void *_pad1; } NormalEditModifierData; /* NormalEditModifierData.mode */ @@ -2154,6 +2174,7 @@ typedef struct MeshSeqCacheModifierData { float last_lookup_time; int _pad1; + void *_pad2; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ @@ -2198,6 +2219,7 @@ typedef struct SurfaceDeformModifierData { float mat[4][4]; float strength; char defgrp_name[64]; + void *_pad1; } SurfaceDeformModifierData; /* Surface Deform modifier flags */ @@ -2259,6 +2281,7 @@ typedef struct NodesModifierData { /* Contains logged information from the last evaluation. This can be used to help the user to * debug a node tree. */ void *runtime_eval_log; + void *_pad1; } NodesModifierData; typedef struct MeshToVolumeModifierData { @@ -2286,6 +2309,7 @@ typedef struct MeshToVolumeModifierData { float density; char _pad2[4]; + void *_pad3; } MeshToVolumeModifierData; /* MeshToVolumeModifierData->resolution_mode */ @@ -2332,6 +2356,7 @@ typedef struct VolumeToMeshModifierData { /** MAX_NAME */ char grid_name[64]; + void *_pad1; } VolumeToMeshModifierData; /** VolumeToMeshModifierData->resolution_mode */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 5152098f57a..fd794ed1b21 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1328,6 +1328,13 @@ typedef struct NodeAttributeConvert { int8_t domain; } NodeAttributeConvert; +typedef struct NodeGeometrySubdivisionSurface { + /* eSubsurfUVSmooth. */ + uint8_t uv_smooth; + /* eSubsurfBoundarySmooth. */ + uint8_t boundary_smooth; +} NodeGeometrySubdivisionSurface; + typedef struct NodeGeometryMeshCircle { /* GeometryNodeMeshCircleFillType. */ uint8_t fill_type; @@ -1355,6 +1362,11 @@ typedef struct NodeSwitch { uint8_t input_type; } NodeSwitch; +typedef struct NodeGeometryCurveSplineType { + /* GeometryNodeSplineType. */ + uint8_t spline_type; +} NodeGeometryCurveSplineType; + typedef struct NodeGeometryCurveSetHandles { /* GeometryNodeCurveHandleType. */ uint8_t handle_type; @@ -1362,6 +1374,13 @@ typedef struct NodeGeometryCurveSetHandles { uint8_t mode; } NodeGeometryCurveSetHandles; +typedef struct NodeGeometryCurveSelectHandles { + /* GeometryNodeCurveHandleType. */ + uint8_t handle_type; + /* GeometryNodeCurveHandleMode. */ + uint8_t mode; +} NodeGeometryCurveSelectHandles; + typedef struct NodeGeometryCurvePrimitiveLine { /* GeometryNodeCurvePrimitiveLineMode. */ uint8_t mode; @@ -1828,6 +1847,12 @@ typedef enum GeometryNodeBooleanOperation { GEO_NODE_BOOLEAN_DIFFERENCE = 2, } GeometryNodeBooleanOperation; +typedef enum GeometryNodeSplineType { + GEO_NODE_SPLINE_TYPE_BEZIER = 0, + GEO_NODE_SPLINE_TYPE_NURBS = 1, + GEO_NODE_SPLINE_TYPE_POLY = 2, +} GeometryNodeSplineType; + typedef enum GeometryNodeCurvePrimitiveCircleMode { GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS = 0, GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 60a34fef899..0250d853898 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -385,14 +385,14 @@ typedef struct Object { short softflag; /** For restricting view, select, render etc. accessible in outliner. */ - char restrictflag; + short visibility_flag; - /** Flag for pinning. */ - char shapeflag; /** Current shape key for menu or pinned. */ short shapenr; + /** Flag for pinning. */ + char shapeflag; - char _pad3[2]; + char _pad3[1]; /** Object constraints. */ ListBase constraints; @@ -433,6 +433,7 @@ typedef struct Object { ObjectLineArt lineart; /** Runtime evaluation data (keep last). */ + void *_pad9; Object_Runtime runtime; } Object; @@ -466,8 +467,6 @@ typedef struct ObHook { /* used many places, should be specialized. */ #define SELECT 1 -#define OBJECT_ACTIVE_MODIFIER_NONE -1 - /* type */ enum { OB_EMPTY = 0, @@ -670,11 +669,19 @@ enum { # define OB_FLAG_UNUSED_12 (1 << 12) /* cleared */ #endif -/* ob->restrictflag */ +/* ob->visibility_flag */ enum { - OB_RESTRICT_VIEWPORT = 1 << 0, - OB_RESTRICT_SELECT = 1 << 1, - OB_RESTRICT_RENDER = 1 << 2, + OB_HIDE_VIEWPORT = 1 << 0, + OB_HIDE_SELECT = 1 << 1, + OB_HIDE_RENDER = 1 << 2, + OB_HIDE_CAMERA = 1 << 3, + OB_HIDE_DIFFUSE = 1 << 4, + OB_HIDE_GLOSSY = 1 << 5, + OB_HIDE_TRANSMISSION = 1 << 6, + OB_HIDE_VOLUME_SCATTER = 1 << 7, + OB_HIDE_SHADOW = 1 << 8, + OB_HOLDOUT = 1 << 9, + OB_SHADOW_CATCHER = 1 << 10 }; /* ob->shapeflag */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index af524ff4866..df18501d2ea 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -296,6 +296,7 @@ typedef struct Editing { int64_t disk_cache_timestamp; EditingRuntime runtime; + void *_pad1; } Editing; /* ************* Effect Variable Structs ********* */ diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h index b2bb50c56a2..e6394f0a56a 100644 --- a/source/blender/makesdna/DNA_sound_types.h +++ b/source/blender/makesdna/DNA_sound_types.h @@ -67,6 +67,7 @@ typedef struct bSound { /** Runtime only, always reset in readfile. */ short tags; char _pad[4]; + double offset_time; /* Unused currently. */ // int type; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7290647dbc6..863c53615c1 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -754,13 +754,7 @@ typedef struct FileSelectParams { /** Max number of levels in dirtree to show at once, 0 to disable recursion. */ short recursion_level; - /* XXX --- still unused -- */ - /** Show font preview. */ - short f_fp; - /** String to use for font preview. */ - char fp_str[8]; - - /* XXX --- end unused -- */ + char _pad4[2]; } FileSelectParams; /** @@ -769,7 +763,7 @@ typedef struct FileSelectParams { typedef struct FileAssetSelectParams { FileSelectParams base_params; - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; short import_type; /* eFileAssetImportType */ char _pad[6]; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 5f8a8c6230a..27376432092 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -71,14 +71,13 @@ typedef struct uiFontStyle { short uifont_id; /** Actual size depends on 'global' dpi. */ short points; - /** Unfitted or default kerning value. */ - short kerning; /** Style hint. */ short italic, bold; /** Value is amount of pixels blur. */ short shadow; /** Shadow offset in pixels. */ short shadx, shady; + char _pad0[2]; /** Total alpha. */ float shadowalpha; /** 1 value, typically white or black anyway. */ @@ -645,7 +644,7 @@ typedef struct UserDef_Experimental { char use_full_frame_compositor; char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; - char use_asset_browser; + char use_extended_asset_browser; char use_override_templates; char _pad[5]; /** `makesdna` does not allow empty structs. */ diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index 9dfc37e57b1..c4d0c83b346 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -71,6 +71,7 @@ .gpencil_paper_opacity = 0.5f, \ .gpencil_grid_opacity = 0.9f, \ .gpencil_vertex_paint_opacity = 1.0f, \ + .normals_constant_screen_size = 7.0f, \ } #define _DNA_DEFAULT_View3DCursor \ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 08b29c82707..4d88f6f0c15 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -204,6 +204,7 @@ typedef struct View3DOverlay { /** Edit mode settings. */ int edit_flag; float normals_length; + float normals_constant_screen_size; float backwire_opacity; /** Paint mode settings. */ @@ -238,6 +239,8 @@ typedef struct View3DOverlay { float gpencil_vertex_paint_opacity; /** Handles display type for curves. */ int handle_display; + + char _pad[4]; } View3DOverlay; /* View3DOverlay->handle_display */ @@ -551,6 +554,7 @@ enum { // V3D_OVERLAY_EDIT_CU_HANDLES = (1 << 20), V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21), + V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22), }; /** #View3DOverlay.paint_flag */ diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 2bac040ea90..e0294d3534c 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -139,7 +139,7 @@ typedef struct WorkSpace { /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The * Asset Browser has its own and doesn't use this. */ - AssetLibraryReference asset_library; + AssetLibraryReference asset_library_ref; } WorkSpace; /** diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index fc00d5eb839..a9d427777f7 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -26,13 +26,15 @@ extern "C" { #endif +/* -------------------------------------------------------------------- */ + typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; char _pad[7]; - char base_pose_type; /* eXRSessionBasePoseType */ + char base_pose_type; /* #eXRSessionBasePoseType */ /** Object to take the location and rotation as base position from. */ Object *base_pose_object; float base_pose_location[3]; @@ -68,12 +70,126 @@ typedef enum eXrActionType { XR_VIBRATION_OUTPUT = 100, } eXrActionType; +/** Determines how XR action operators are executed. */ typedef enum eXrOpFlag { XR_OP_PRESS = 0, XR_OP_RELEASE = 1, XR_OP_MODAL = 2, } eXrOpFlag; +typedef enum eXrActionFlag { + /** Action depends on two sub-action paths (i.e. two-handed/bi-manual action). */ + XR_ACTION_BIMANUAL = (1 << 0), +} eXrActionFlag; + +typedef enum eXrHapticFlag { + /** Whether to apply haptics to corresponding user paths for an action and its haptic action. */ + XR_HAPTIC_MATCHUSERPATHS = (1 << 0), + /** + * Determines how haptics will be applied + * ("repeat" is mutually exclusive with "press"/"release"). + */ + XR_HAPTIC_PRESS = (1 << 1), + XR_HAPTIC_RELEASE = (1 << 2), + XR_HAPTIC_REPEAT = (1 << 3), +} eXrHapticFlag; + +/** + * For axis-based inputs (thumb-stick/track-pad/etc). + * Determines the region for action execution (mutually exclusive per axis). + */ +typedef enum eXrAxisFlag { + XR_AXIS0_POS = (1 << 0), + XR_AXIS0_NEG = (1 << 1), + XR_AXIS1_POS = (1 << 2), + XR_AXIS1_NEG = (1 << 3), +} eXrAxisFlag; + +typedef enum eXrPoseFlag { + /* Pose represents controller grip/aim. */ + XR_POSE_GRIP = (1 << 0), + XR_POSE_AIM = (1 << 1), +} eXrPoseFlag; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapBinding { + struct XrActionMapBinding *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + /** OpenXR interaction profile path. */ + char profile[256]; + /** OpenXR component paths. */ + char component_path0[192]; + char component_path1[192]; + + /** Input threshold/region. */ + float float_threshold; + short axis_flag; /* eXrAxisFlag */ + char _pad[2]; + + /** Pose action properties. */ + float pose_location[3]; + float pose_rotation[3]; +} XrActionMapBinding; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMapItem { + struct XrActionMapItem *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + /** Type. */ + char type; /** eXrActionType */ + char _pad[7]; + + /** OpenXR user paths. */ + char user_path0[64]; + char user_path1[64]; + + /** Operator to be called on XR events. */ + char op[64]; /* OP_MAX_TYPENAME */ + /** Operator properties, assigned to ptr->data and can be written to a file. */ + IDProperty *op_properties; + /** RNA pointer to access properties. */ + struct PointerRNA *op_properties_ptr; + + short op_flag; /* eXrOpFlag */ + short action_flag; /* eXrActionFlag */ + short haptic_flag; /* eXrHapticFlag */ + + /** Pose action properties. */ + short pose_flag; /* eXrPoseFlag */ + + /** Haptic properties. */ + char haptic_name[64]; /* MAX_NAME */ + float haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + short selbinding; + char _pad3[2]; + ListBase bindings; /* XrActionMapBinding */ +} XrActionMapItem; + +/* -------------------------------------------------------------------- */ + +typedef struct XrActionMap { + struct XrActionMap *next, *prev; + + /** Unique name. */ + char name[64]; /* MAX_NAME */ + + ListBase items; /* XrActionMapItem */ + short selitem; + char _pad[6]; +} XrActionMap; + +/* -------------------------------------------------------------------- */ + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index d363e40e4f0..2feebbfd4f4 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -82,11 +82,13 @@ DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_vel_factor, guide_vel_factor DNA_STRUCT_RENAME_ELEM(FluidEffectorSettings, guiding_mode, guide_mode) DNA_STRUCT_RENAME_ELEM(Image, name, filepath) DNA_STRUCT_RENAME_ELEM(Library, name, filepath) +DNA_STRUCT_RENAME_ELEM(MaskLayer, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(MovieClip, name, filepath) DNA_STRUCT_RENAME_ELEM(Object, col, color) DNA_STRUCT_RENAME_ELEM(Object, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale) DNA_STRUCT_RENAME_ELEM(Object, size, scale) +DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index f2a75a60a44..061c3462a69 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -165,6 +165,10 @@ static char **names; static char **types; /** At `types_size[a]` is the size of type `a` on this systems bitness (32 or 64). */ static short *types_size_native; +/** Contains align requirements for a struct on 32 bit systems. */ +static short *types_align_32; +/** Contains align requirements for a struct on 64 bit systems. */ +static short *types_align_64; /** Contains sizes as they are calculated on 32 bit systems. */ static short *types_size_32; /** Contains sizes as they are calculated on 64 bit systems. */ @@ -406,6 +410,8 @@ static int add_type(const char *str, int size) types_size_native[index] = size; types_size_32[index] = size; types_size_64[index] = size; + types_align_32[index] = size; + types_align_64[index] = size; } return index; } @@ -419,7 +425,8 @@ static int add_type(const char *str, int size) types_size_native[types_len] = size; types_size_32[types_len] = size; types_size_64[types_len] = size; - + types_align_32[types_len] = size; + types_align_64[types_len] = size; if (types_len >= max_array_len) { printf("too many types\n"); return types_len - 1; @@ -966,7 +973,9 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char int size_native = 0; int size_32 = 0; int size_64 = 0; - bool has_pointer = false; + /* Sizes of the largest field in a struct. */ + int max_align_32 = 0; + int max_align_64 = 0; /* check all elements in struct */ for (int b = 0; b < structpoin[1]; b++, sp += 2) { @@ -995,7 +1004,6 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char /* is it a pointer or function pointer? */ if (cp[0] == '*' || cp[1] == '*') { - has_pointer = 1; /* has the name an extra length? (array) */ int mul = 1; if (cp[namelen - 1] == ']') { @@ -1042,6 +1050,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char size_native += sizeof(void *) * mul; size_32 += 4 * mul; size_64 += 8 * mul; + max_align_32 = MAX2(max_align_32, 4); + max_align_64 = MAX2(max_align_64, 8); } else if (cp[0] == '[') { /* parsing can cause names "var" and "[3]" @@ -1087,6 +1097,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char size_native += mul * types_size_native[type]; size_32 += mul * types_size_32[type]; size_64 += mul * types_size_64[type]; + max_align_32 = MAX2(max_align_32, types_align_32[type]); + max_align_64 = MAX2(max_align_64, types_align_64[type]); } else { size_native = 0; @@ -1103,16 +1115,42 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char types_size_native[structtype] = size_native; types_size_32[structtype] = size_32; types_size_64[structtype] = size_64; - /* Two ways to detect if a struct contains a pointer: - * has_pointer is set or size_native doesn't match any of 32/64bit lengths. */ - if (has_pointer || size_64 != size_native || size_32 != size_native) { - if (size_64 % 8) { + types_align_32[structtype] = max_align_32; + types_align_64[structtype] = max_align_64; + + /* Sanity check 1: alignment should never be 0. */ + BLI_assert(max_align_32); + BLI_assert(max_align_64); + + /* Sanity check 2: alignment should always be equal or smaller than the maximum + * size of a build in type which is 8 bytes (ie int64_t or double). */ + BLI_assert(max_align_32 <= 8); + BLI_assert(max_align_64 <= 8); + + if (size_32 % max_align_32) { + /* There is an one odd case where only the 32 bit struct has alignment issues + * and the 64 bit does not, that can only be fixed by adding a padding pointer + * to the struct to resolve the problem. */ + if ((size_64 % max_align_64 == 0) && (size_32 % max_align_32 == 4)) { fprintf(stderr, - "Sizeerror 8 in struct: %s (add %d bytes)\n", + "Sizeerror in 32 bit struct: %s (add paddding pointer)\n", + types[structtype]); + } + else { + fprintf(stderr, + "Sizeerror in 32 bit struct: %s (add %d bytes)\n", types[structtype], - size_64 % 8); - dna_error = 1; + max_align_32 - (size_32 % max_align_32)); } + dna_error = 1; + } + + if (size_64 % max_align_64) { + fprintf(stderr, + "Sizeerror in 64 bit struct: %s (add %d bytes)\n", + types[structtype], + max_align_64 - (size_64 % max_align_64)); + dna_error = 1; } if (size_native % 4 && !ELEM(size_native, 1, 2)) { @@ -1229,6 +1267,9 @@ static int make_structDNA(const char *base_directory, types_size_native = MEM_callocN(sizeof(short) * max_array_len, "types_size_native"); types_size_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32"); types_size_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64"); + types_align_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32"); + types_align_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64"); + structs = MEM_callocN(sizeof(short *) * max_array_len, "structs"); /* Build versioning data */ @@ -1317,7 +1358,11 @@ static int make_structDNA(const char *base_directory, sp += 2; /* ? num_types was elem? */ for (b = 0; b < num_types; b++, sp += 2) { - printf(" %s %s\n", types[sp[0]], names[sp[1]]); + printf(" %s %s allign32:%d, allign64:%d\n", + types[sp[0]], + names[sp[1]], + types_align_32[sp[0]], + types_align_64[sp[0]]); } } } @@ -1439,6 +1484,8 @@ static int make_structDNA(const char *base_directory, MEM_freeN(types_size_native); MEM_freeN(types_size_32); MEM_freeN(types_size_64); + MEM_freeN(types_align_32); + MEM_freeN(types_align_64); MEM_freeN(structs); BLI_memarena_free(mem_arena); diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 97615016016..f206bde4705 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -499,6 +499,7 @@ extern StructRNA RNA_Pose; extern StructRNA RNA_PoseBone; extern StructRNA RNA_Preferences; extern StructRNA RNA_PreferencesEdit; +extern StructRNA RNA_PreferencesExperimental; extern StructRNA RNA_PreferencesFilePaths; extern StructRNA RNA_PreferencesInput; extern StructRNA RNA_PreferencesKeymap; @@ -827,6 +828,7 @@ unsigned int RNA_struct_count_properties(StructRNA *srna); /* lower level functions for access to type properties */ const struct ListBase *RNA_struct_type_properties(StructRNA *srna); +PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier); PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier); FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier); diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h new file mode 100644 index 00000000000..c8f44262020 --- /dev/null +++ b/source/blender/makesrna/RNA_enum_items.h @@ -0,0 +1,240 @@ +/* + * 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 RNA + */ + +/* NOTE: this is included multiple times with different #defines for DEF_ENUM. */ + +/* use in cases where only dynamic types are used */ +DEF_ENUM(DummyRNA_NULL_items) +DEF_ENUM(DummyRNA_DEFAULT_items) + +/* all others should follow 'rna_enum_*_items' naming */ +DEF_ENUM(rna_enum_id_type_items) + +DEF_ENUM(rna_enum_object_mode_items) +DEF_ENUM(rna_enum_workspace_object_mode_items) +DEF_ENUM(rna_enum_object_empty_drawtype_items) +DEF_ENUM(rna_enum_object_gpencil_type_items) +DEF_ENUM(rna_enum_metaelem_type_items) + +DEF_ENUM(rna_enum_proportional_falloff_items) +DEF_ENUM(rna_enum_proportional_falloff_curve_only_items) +DEF_ENUM(rna_enum_snap_target_items) +DEF_ENUM(rna_enum_snap_element_items) +DEF_ENUM(rna_enum_snap_node_element_items) +DEF_ENUM(rna_enum_curve_fit_method_items) +DEF_ENUM(rna_enum_mesh_select_mode_items) +DEF_ENUM(rna_enum_mesh_select_mode_uv_items) +DEF_ENUM(rna_enum_mesh_delimit_mode_items) +DEF_ENUM(rna_enum_space_graph_mode_items) +DEF_ENUM(rna_enum_space_file_browse_mode_items) +DEF_ENUM(rna_enum_space_sequencer_view_type_items) +DEF_ENUM(rna_enum_space_type_items) +DEF_ENUM(rna_enum_space_image_mode_items) +DEF_ENUM(rna_enum_space_image_mode_all_items) +DEF_ENUM(rna_enum_space_action_mode_items) +DEF_ENUM(rna_enum_fileselect_params_sort_items) +DEF_ENUM(rna_enum_region_type_items) +DEF_ENUM(rna_enum_object_modifier_type_items) +DEF_ENUM(rna_enum_constraint_type_items) +DEF_ENUM(rna_enum_boidrule_type_items) +DEF_ENUM(rna_enum_sequence_modifier_type_items) +DEF_ENUM(rna_enum_object_greasepencil_modifier_type_items) +DEF_ENUM(rna_enum_object_shaderfx_type_items) + +DEF_ENUM(rna_enum_modifier_triangulate_quad_method_items) +DEF_ENUM(rna_enum_modifier_triangulate_ngon_method_items) +DEF_ENUM(rna_enum_modifier_shrinkwrap_mode_items) + +DEF_ENUM(rna_enum_image_type_items) +DEF_ENUM(rna_enum_image_color_mode_items) +DEF_ENUM(rna_enum_image_color_depth_items) +DEF_ENUM(rna_enum_image_generated_type_items) + +DEF_ENUM(rna_enum_normal_space_items) +DEF_ENUM(rna_enum_normal_swizzle_items) +DEF_ENUM(rna_enum_bake_save_mode_items) +DEF_ENUM(rna_enum_bake_target_items) + +DEF_ENUM(rna_enum_views_format_items) +DEF_ENUM(rna_enum_views_format_multilayer_items) +DEF_ENUM(rna_enum_views_format_multiview_items) +DEF_ENUM(rna_enum_stereo3d_display_items) +DEF_ENUM(rna_enum_stereo3d_anaglyph_type_items) +DEF_ENUM(rna_enum_stereo3d_interlace_type_items) + +#ifdef WITH_OPENEXR +DEF_ENUM(rna_enum_exr_codec_items) +#endif +DEF_ENUM(rna_enum_color_sets_items) + +DEF_ENUM(rna_enum_beztriple_keyframe_type_items) +DEF_ENUM(rna_enum_beztriple_interpolation_mode_items) +DEF_ENUM(rna_enum_beztriple_interpolation_easing_items) +DEF_ENUM(rna_enum_fcurve_auto_smoothing_items) +DEF_ENUM(rna_enum_keyframe_handle_type_items) +DEF_ENUM(rna_enum_driver_target_rotation_mode_items) + +DEF_ENUM(rna_enum_keyingset_path_grouping_items) +DEF_ENUM(rna_enum_keying_flag_items) +DEF_ENUM(rna_enum_keying_flag_items_api) + +DEF_ENUM(rna_enum_fmodifier_type_items) + +DEF_ENUM(rna_enum_motionpath_bake_location_items) + +DEF_ENUM(rna_enum_event_value_all_items) +DEF_ENUM(rna_enum_event_value_keymouse_items) +DEF_ENUM(rna_enum_event_value_tweak_items) + +DEF_ENUM(rna_enum_event_type_items) +DEF_ENUM(rna_enum_event_type_mask_items) + +DEF_ENUM(rna_enum_operator_type_flag_items) +DEF_ENUM(rna_enum_operator_return_items) +DEF_ENUM(rna_enum_operator_property_tags) + +DEF_ENUM(rna_enum_brush_sculpt_tool_items) +DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items) +DEF_ENUM(rna_enum_brush_vertex_tool_items) +DEF_ENUM(rna_enum_brush_weight_tool_items) +DEF_ENUM(rna_enum_brush_gpencil_types_items) +DEF_ENUM(rna_enum_brush_gpencil_vertex_types_items) +DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items) +DEF_ENUM(rna_enum_brush_gpencil_weight_types_items) +DEF_ENUM(rna_enum_brush_image_tool_items) + +DEF_ENUM(rna_enum_axis_xy_items) +DEF_ENUM(rna_enum_axis_xyz_items) + +DEF_ENUM(rna_enum_axis_flag_xyz_items) + +DEF_ENUM(rna_enum_symmetrize_direction_items) + +DEF_ENUM(rna_enum_texture_type_items) + +DEF_ENUM(rna_enum_light_type_items) + +DEF_ENUM(rna_enum_lightprobes_type_items) + +DEF_ENUM(rna_enum_unpack_method_items) + +DEF_ENUM(rna_enum_object_type_items) +DEF_ENUM(rna_enum_object_rotation_mode_items) + +DEF_ENUM(rna_enum_object_type_curve_items) + +DEF_ENUM(rna_enum_rigidbody_object_type_items) +DEF_ENUM(rna_enum_rigidbody_object_shape_items) +DEF_ENUM(rna_enum_rigidbody_constraint_type_items) + +DEF_ENUM(rna_enum_object_axis_items) + +DEF_ENUM(rna_enum_render_pass_type_items) + +DEF_ENUM(rna_enum_bake_pass_type_items) +DEF_ENUM(rna_enum_bake_pass_filter_type_items) + +DEF_ENUM(rna_enum_keymap_propvalue_items) + +DEF_ENUM(rna_enum_operator_context_items) + +DEF_ENUM(rna_enum_wm_report_items) + +DEF_ENUM(rna_enum_property_type_items) +DEF_ENUM(rna_enum_property_subtype_items) +DEF_ENUM(rna_enum_property_unit_items) + +DEF_ENUM(rna_enum_shading_type_items) + +DEF_ENUM(rna_enum_navigation_mode_items) + +DEF_ENUM(rna_enum_node_socket_in_out_items) + +DEF_ENUM(rna_enum_node_math_items) +DEF_ENUM(rna_enum_mapping_type_items) +DEF_ENUM(rna_enum_node_vec_math_items) +DEF_ENUM(rna_enum_node_boolean_math_items) +DEF_ENUM(rna_enum_node_float_compare_items) +DEF_ENUM(rna_enum_node_filter_items) +DEF_ENUM(rna_enum_node_float_to_int_items) +DEF_ENUM(rna_enum_node_map_range_items) +DEF_ENUM(rna_enum_node_clamp_items) + +DEF_ENUM(rna_enum_ramp_blend_items) + +DEF_ENUM(rna_enum_prop_dynamicpaint_type_items) + +DEF_ENUM(rna_enum_clip_editor_mode_items) + +DEF_ENUM(rna_enum_icon_items) +DEF_ENUM(rna_enum_uilist_layout_type_items) + +DEF_ENUM(rna_enum_linestyle_color_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_alpha_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_thickness_modifier_type_items) +DEF_ENUM(rna_enum_linestyle_geometry_modifier_type_items) + +DEF_ENUM(rna_enum_window_cursor_items) + +DEF_ENUM(rna_enum_dt_method_vertex_items) +DEF_ENUM(rna_enum_dt_method_edge_items) +DEF_ENUM(rna_enum_dt_method_loop_items) +DEF_ENUM(rna_enum_dt_method_poly_items) +DEF_ENUM(rna_enum_dt_mix_mode_items) +DEF_ENUM(rna_enum_dt_layers_select_src_items) +DEF_ENUM(rna_enum_dt_layers_select_dst_items) + +DEF_ENUM(rna_enum_context_mode_items) + +DEF_ENUM(rna_enum_preference_section_items) + +DEF_ENUM(rna_enum_attribute_type_items) +DEF_ENUM(rna_enum_attribute_type_with_auto_items) +DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_attribute_domain_with_auto_items) + +DEF_ENUM(rna_enum_collection_color_items) + +DEF_ENUM(rna_enum_subdivision_uv_smooth_items) +DEF_ENUM(rna_enum_subdivision_boundary_smooth_items) + +DEF_ENUM(rna_enum_transform_orientation_items) + +/* Not available to RNA pre-processing (`makrsrna`). + * Defined in editors for example. */ +#ifndef RNA_MAKESRNA + +DEF_ENUM(rna_enum_particle_edit_hair_brush_items) +DEF_ENUM(rna_enum_particle_edit_disconnected_hair_brush_items) + +DEF_ENUM(rna_enum_keyframe_paste_offset_items) +DEF_ENUM(rna_enum_keyframe_paste_merge_items) + +DEF_ENUM(rna_enum_transform_pivot_items_full) +DEF_ENUM(rna_enum_transform_mode_types) + +/* In the runtime part of RNA, could be removed from this section. */ +DEF_ENUM(rna_enum_nla_mode_extend_items) +DEF_ENUM(rna_enum_nla_mode_blend_items) +DEF_ENUM(rna_enum_keyblock_type_items) + +#endif + +#undef DEF_ENUM diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d544083a749..d7520834287 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -32,219 +32,11 @@ struct bNodeTreeType; struct bNodeType; /* Types */ +#define DEF_ENUM(id) extern const EnumPropertyItem id[]; +#include "RNA_enum_items.h" -/* use in cases where only dynamic types are used */ -extern const EnumPropertyItem DummyRNA_NULL_items[]; -extern const EnumPropertyItem DummyRNA_DEFAULT_items[]; - -/* all others should follow 'rna_enum_*_items' naming */ -extern const EnumPropertyItem rna_enum_id_type_items[]; - -extern const EnumPropertyItem rna_enum_object_mode_items[]; -extern const EnumPropertyItem rna_enum_workspace_object_mode_items[]; -extern const EnumPropertyItem rna_enum_object_empty_drawtype_items[]; -extern const EnumPropertyItem rna_enum_object_gpencil_type_items[]; -extern const EnumPropertyItem rna_enum_metaelem_type_items[]; - -extern const EnumPropertyItem rna_enum_proportional_falloff_items[]; -extern const EnumPropertyItem rna_enum_proportional_falloff_curve_only_items[]; -extern const EnumPropertyItem rna_enum_snap_target_items[]; -extern const EnumPropertyItem rna_enum_snap_element_items[]; -extern const EnumPropertyItem rna_enum_snap_node_element_items[]; -extern const EnumPropertyItem rna_enum_curve_fit_method_items[]; -extern const EnumPropertyItem rna_enum_mesh_select_mode_items[]; -extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]; -extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]; -extern const EnumPropertyItem rna_enum_space_graph_mode_items[]; -extern const EnumPropertyItem rna_enum_space_file_browse_mode_items[]; -extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[]; -extern const EnumPropertyItem rna_enum_space_type_items[]; -extern const EnumPropertyItem rna_enum_space_image_mode_items[]; -extern const EnumPropertyItem rna_enum_space_image_mode_all_items[]; -extern const EnumPropertyItem rna_enum_space_action_mode_items[]; -extern const EnumPropertyItem rna_enum_fileselect_params_sort_items[]; -extern const EnumPropertyItem rna_enum_region_type_items[]; -extern const EnumPropertyItem rna_enum_object_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_constraint_type_items[]; -extern const EnumPropertyItem rna_enum_boidrule_type_items[]; -extern const EnumPropertyItem rna_enum_sequence_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_object_shaderfx_type_items[]; - -extern const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[]; -extern const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[]; -extern const EnumPropertyItem rna_enum_modifier_shrinkwrap_mode_items[]; - -extern const EnumPropertyItem rna_enum_image_type_items[]; -extern const EnumPropertyItem rna_enum_image_color_mode_items[]; -extern const EnumPropertyItem rna_enum_image_color_depth_items[]; -extern const EnumPropertyItem rna_enum_image_generated_type_items[]; - -extern const EnumPropertyItem rna_enum_normal_space_items[]; -extern const EnumPropertyItem rna_enum_normal_swizzle_items[]; -extern const EnumPropertyItem rna_enum_bake_save_mode_items[]; -extern const EnumPropertyItem rna_enum_bake_target_items[]; - -extern const EnumPropertyItem rna_enum_views_format_items[]; -extern const EnumPropertyItem rna_enum_views_format_multilayer_items[]; -extern const EnumPropertyItem rna_enum_views_format_multiview_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_display_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_anaglyph_type_items[]; -extern const EnumPropertyItem rna_enum_stereo3d_interlace_type_items[]; - -extern const EnumPropertyItem rna_enum_exr_codec_items[]; -extern const EnumPropertyItem rna_enum_color_sets_items[]; - -extern const EnumPropertyItem rna_enum_beztriple_keyframe_type_items[]; -extern const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[]; -extern const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[]; -extern const EnumPropertyItem rna_enum_fcurve_auto_smoothing_items[]; -extern const EnumPropertyItem rna_enum_keyframe_handle_type_items[]; -extern const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[]; - -extern const EnumPropertyItem rna_enum_keyblock_type_items[]; - -extern const EnumPropertyItem rna_enum_keyingset_path_grouping_items[]; -extern const EnumPropertyItem rna_enum_keying_flag_items[]; -extern const EnumPropertyItem rna_enum_keying_flag_items_api[]; - -extern const EnumPropertyItem rna_enum_keyframe_paste_offset_items[]; -extern const EnumPropertyItem rna_enum_keyframe_paste_merge_items[]; - -extern const EnumPropertyItem rna_enum_fmodifier_type_items[]; - -extern const EnumPropertyItem rna_enum_nla_mode_extend_items[]; -extern const EnumPropertyItem rna_enum_nla_mode_blend_items[]; - -extern const EnumPropertyItem rna_enum_motionpath_bake_location_items[]; - -extern const EnumPropertyItem rna_enum_event_value_all_items[]; -extern const EnumPropertyItem rna_enum_event_value_keymouse_items[]; -extern const EnumPropertyItem rna_enum_event_value_tweak_items[]; - -extern const EnumPropertyItem rna_enum_event_type_items[]; -extern const EnumPropertyItem rna_enum_event_type_mask_items[]; - -extern const EnumPropertyItem rna_enum_operator_type_flag_items[]; -extern const EnumPropertyItem rna_enum_operator_return_items[]; -extern const EnumPropertyItem rna_enum_operator_property_tags[]; - -extern const EnumPropertyItem rna_enum_brush_sculpt_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_uv_sculpt_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_vertex_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_weight_tool_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_vertex_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_sculpt_types_items[]; -extern const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[]; -extern const EnumPropertyItem rna_enum_brush_image_tool_items[]; - -extern const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[]; -extern const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[]; - -extern const EnumPropertyItem rna_enum_uv_sculpt_tool_items[]; - -extern const EnumPropertyItem rna_enum_axis_xy_items[]; -extern const EnumPropertyItem rna_enum_axis_xyz_items[]; - -extern const EnumPropertyItem rna_enum_axis_flag_xyz_items[]; - -extern const EnumPropertyItem rna_enum_symmetrize_direction_items[]; - -extern const EnumPropertyItem rna_enum_texture_type_items[]; - -extern const EnumPropertyItem rna_enum_light_type_items[]; - -extern const EnumPropertyItem rna_enum_lightprobes_type_items[]; - -extern const EnumPropertyItem rna_enum_unpack_method_items[]; - -extern const EnumPropertyItem rna_enum_object_type_items[]; -extern const EnumPropertyItem rna_enum_object_rotation_mode_items[]; - -extern const EnumPropertyItem rna_enum_object_type_curve_items[]; - -extern const EnumPropertyItem rna_enum_rigidbody_object_type_items[]; -extern const EnumPropertyItem rna_enum_rigidbody_object_shape_items[]; -extern const EnumPropertyItem rna_enum_rigidbody_constraint_type_items[]; - -extern const EnumPropertyItem rna_enum_object_axis_items[]; - -extern const EnumPropertyItem rna_enum_controller_type_items[]; - -extern const EnumPropertyItem rna_enum_render_pass_type_items[]; -extern const EnumPropertyItem rna_enum_render_pass_debug_type_items[]; - -extern const EnumPropertyItem rna_enum_bake_pass_type_items[]; -extern const EnumPropertyItem rna_enum_bake_pass_filter_type_items[]; - -extern const EnumPropertyItem rna_enum_keymap_propvalue_items[]; - -extern const EnumPropertyItem rna_enum_operator_context_items[]; - -extern const EnumPropertyItem rna_enum_wm_report_items[]; - -extern const EnumPropertyItem rna_enum_transform_pivot_items_full[]; -extern const EnumPropertyItem rna_enum_transform_orientation_items[]; -extern const EnumPropertyItem rna_enum_transform_mode_types[]; - -extern const EnumPropertyItem rna_enum_property_type_items[]; -extern const EnumPropertyItem rna_enum_property_subtype_items[]; -extern const EnumPropertyItem rna_enum_property_unit_items[]; - -extern const EnumPropertyItem rna_enum_shading_type_items[]; - -extern const EnumPropertyItem rna_enum_navigation_mode_items[]; - -extern const EnumPropertyItem rna_enum_node_socket_in_out_items[]; - -extern const EnumPropertyItem rna_enum_node_math_items[]; -extern const EnumPropertyItem rna_enum_mapping_type_items[]; -extern const EnumPropertyItem rna_enum_node_vec_math_items[]; -extern const EnumPropertyItem rna_enum_node_boolean_math_items[]; -extern const EnumPropertyItem rna_enum_node_float_compare_items[]; -extern const EnumPropertyItem rna_enum_node_filter_items[]; -extern const EnumPropertyItem rna_enum_node_float_to_int_items[]; -extern const EnumPropertyItem rna_enum_node_map_range_items[]; -extern const EnumPropertyItem rna_enum_node_clamp_items[]; - -extern const EnumPropertyItem rna_enum_ramp_blend_items[]; - -extern const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[]; - -extern const EnumPropertyItem rna_enum_clip_editor_mode_items[]; - -extern const EnumPropertyItem rna_enum_icon_items[]; -extern const EnumPropertyItem rna_enum_uilist_layout_type_items[]; - -extern const EnumPropertyItem rna_enum_linestyle_color_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_alpha_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_thickness_modifier_type_items[]; -extern const EnumPropertyItem rna_enum_linestyle_geometry_modifier_type_items[]; - -extern const EnumPropertyItem rna_enum_window_cursor_items[]; - -extern const EnumPropertyItem rna_enum_dt_method_vertex_items[]; -extern const EnumPropertyItem rna_enum_dt_method_edge_items[]; -extern const EnumPropertyItem rna_enum_dt_method_loop_items[]; -extern const EnumPropertyItem rna_enum_dt_method_poly_items[]; -extern const EnumPropertyItem rna_enum_dt_mix_mode_items[]; -extern const EnumPropertyItem rna_enum_dt_layers_select_src_items[]; -extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[]; - -extern const EnumPropertyItem rna_enum_context_mode_items[]; - -extern const EnumPropertyItem rna_enum_curveprofile_preset_items[]; -extern const EnumPropertyItem rna_enum_preference_section_items[]; - -extern const EnumPropertyItem rna_enum_attribute_type_items[]; -extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[]; -extern const EnumPropertyItem rna_enum_attribute_domain_items[]; -extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[]; extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); -extern const EnumPropertyItem rna_enum_collection_color_items[]; - /** * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64 * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 95b7b7e5406..7e6d0aea2ee 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -175,6 +175,7 @@ set(SRC_RNA_INC ../RNA_access.h ../RNA_define.h ../RNA_documentation.h + ../RNA_enum_items.h ../RNA_enum_types.h ../RNA_types.h ) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 719b0f73a9d..36f19907080 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -582,6 +582,23 @@ static int rna_color_quantize(PropertyRNA *prop, PropertyDefRNA *dp) (IS_DNATYPE_FLOAT_COMPAT(dp->dnatype) == 0)); } +/** + * Return the identifier for an enum which is defined in "RNA_enum_items.h". + * + * Prevents expanding duplicate enums bloating the binary size. + */ +static const char *rna_enum_id_from_pointer(const EnumPropertyItem *item) +{ +#define RNA_MAKESRNA +#define DEF_ENUM(id) \ + if (item == id) { \ + return STRINGIFY(id); \ + } +#include "RNA_enum_items.h" +#undef RNA_MAKESRNA + return NULL; +} + static const char *rna_function_string(const void *func) { return (func) ? (const char *)func : "NULL"; @@ -3675,37 +3692,55 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr int i, defaultfound = 0, totflag = 0; if (eprop->item) { - fprintf(f, - "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t", - srna->identifier, - strnest, - prop->identifier, - eprop->totitem + 1); - - for (i = 0; i < eprop->totitem; i++) { - fprintf(f, "{%d, ", eprop->item[i].value); - rna_print_c_string(f, eprop->item[i].identifier); - fprintf(f, ", "); - fprintf(f, "%d, ", eprop->item[i].icon); - rna_print_c_string(f, eprop->item[i].name); - fprintf(f, ", "); - rna_print_c_string(f, eprop->item[i].description); - fprintf(f, "},\n\t"); - - if (eprop->item[i].identifier[0]) { - if (prop->flag & PROP_ENUM_FLAG) { - totflag |= eprop->item[i].value; + /* Inline the enum if this is not a defined in "RNA_enum_items.h". */ + const char *item_global_id = rna_enum_id_from_pointer(eprop->item); + if (item_global_id == NULL) { + fprintf(f, + "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t", + srna->identifier, + strnest, + prop->identifier, + eprop->totitem + 1); + + for (i = 0; i < eprop->totitem; i++) { + fprintf(f, "{%d, ", eprop->item[i].value); + rna_print_c_string(f, eprop->item[i].identifier); + fprintf(f, ", "); + fprintf(f, "%d, ", eprop->item[i].icon); + rna_print_c_string(f, eprop->item[i].name); + fprintf(f, ", "); + rna_print_c_string(f, eprop->item[i].description); + fprintf(f, "},\n\t"); + + if (eprop->item[i].identifier[0]) { + if (prop->flag & PROP_ENUM_FLAG) { + totflag |= eprop->item[i].value; + } + else { + if (eprop->defaultvalue == eprop->item[i].value) { + defaultfound = 1; + } + } } - else { - if (eprop->defaultvalue == eprop->item[i].value) { - defaultfound = 1; + } + + fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n"); + } + else { + for (i = 0; i < eprop->totitem; i++) { + if (eprop->item[i].identifier[0]) { + if (prop->flag & PROP_ENUM_FLAG) { + totflag |= eprop->item[i].value; + } + else { + if (eprop->defaultvalue == eprop->item[i].value) { + defaultfound = 1; + } } } } } - fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n"); - if (prop->flag & PROP_ENUM_FLAG) { if (eprop->defaultvalue & ~totflag) { CLOG_ERROR(&LOG, @@ -4047,7 +4082,13 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr rna_function_string(eprop->get_ex), rna_function_string(eprop->set_ex)); if (eprop->item) { - fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier); + const char *item_global_id = rna_enum_id_from_pointer(eprop->item); + if (item_global_id != NULL) { + fprintf(f, "%s, ", item_global_id); + } + else { + fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier); + } } else { fprintf(f, "NULL, "); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 6df03d19538..8f8ad077935 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -741,6 +741,58 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports) BKE_lib_override_library_template_create(id); } +static void rna_ID_override_library_operations_update(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); + return; + } + + BKE_lib_override_library_operations_create(bmain, id); +} + +static void rna_ID_override_library_reset(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports, + bool do_hierarchy) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); + return; + } + + if (do_hierarchy) { + BKE_lib_override_library_id_hierarchy_reset(bmain, id); + } + else { + BKE_lib_override_library_id_reset(bmain, id); + } +} + +static void rna_ID_override_library_destroy(ID *id, + IDOverrideLibrary *UNUSED(override_library), + Main *bmain, + ReportList *reports, + bool do_hierarchy) +{ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name); + return; + } + + if (do_hierarchy) { + BKE_lib_override_library_delete(bmain, id); + } + else { + BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE); + BKE_id_delete(bmain, id); + } +} + static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[]) { @@ -755,6 +807,18 @@ static IDOverrideLibraryProperty *rna_ID_override_library_properties_add( return result; } +static void rna_ID_override_library_properties_remove(IDOverrideLibrary *override_library, + ReportList *reports, + IDOverrideLibraryProperty *override_property) +{ + if (BLI_findindex(&override_library->properties, override_property) == -1) { + BKE_report(reports, RPT_ERROR, "Override property cannot be removed"); + return; + } + + BKE_lib_override_library_property_delete(override_library, override_property); +} + static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add( IDOverrideLibraryProperty *override_property, ReportList *reports, @@ -782,6 +846,19 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper return result; } +static void rna_ID_override_library_property_operations_remove( + IDOverrideLibraryProperty *override_property, + ReportList *reports, + IDOverrideLibraryPropertyOperation *override_operation) +{ + if (BLI_findindex(&override_property->operations, override_operation) == -1) { + BKE_report(reports, RPT_ERROR, "Override operation cannot be removed"); + return; + } + + BKE_lib_override_library_property_operation_delete(override_property, override_operation); +} + static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag) { /* XXX, new function for this! */ @@ -1633,6 +1710,16 @@ static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, Pr "New Operation", "Created operation"); RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_ID_override_library_property_operations_remove"); + RNA_def_function_ui_description(func, "Remove and delete an operation"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, + "operation", + "IDOverrideLibraryPropertyOperation", + "Operation", + "Override operation to be deleted"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } static void rna_def_ID_override_library_property(BlenderRNA *brna) @@ -1689,12 +1776,23 @@ static void rna_def_ID_override_library_properties(BlenderRNA *brna, PropertyRNA parm = RNA_def_string( func, "rna_path", NULL, 256, "RNA Path", "RNA-Path of the property to add"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "remove", "rna_ID_override_library_properties_remove"); + RNA_def_function_ui_description(func, "Remove and delete a property"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, + "property", + "IDOverrideLibraryProperty", + "Property", + "Override property to be deleted"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } static void rna_def_ID_override_library(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL); RNA_def_struct_ui_text( @@ -1710,6 +1808,35 @@ static void rna_def_ID_override_library(BlenderRNA *brna) "List of overridden properties"); rna_def_ID_override_library_properties(brna, prop); + /* Update function. */ + func = RNA_def_function(srna, "operations_update", "rna_ID_override_library_operations_update"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, + "Update the library override operations based on the " + "differences between this override ID and its reference"); + + func = RNA_def_function(srna, "reset", "rna_ID_override_library_reset"); + RNA_def_function_ui_description(func, + "Reset this override to match again its linked reference ID"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_boolean( + func, + "do_hierarchy", + true, + "", + "Also reset all the dependencies of this override to match their reference linked IDs"); + + func = RNA_def_function(srna, "destroy", "rna_ID_override_library_destroy"); + RNA_def_function_ui_description( + func, "Delete this override ID and remap its usages to its linked reference ID instead"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS); + RNA_def_boolean(func, + "do_hierarchy", + true, + "", + "Also delete all the dependencies of this override and remap their usages to " + "their reference linked IDs"); + rna_def_ID_override_library_property(brna); } diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index feacd47c98c..41c31dfebcb 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -36,6 +36,7 @@ #include "BLI_dynstr.h" #include "BLI_ghash.h" #include "BLI_math.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" #include "BLF_api.h" @@ -882,8 +883,7 @@ bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna) PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier) { - if (identifier[0] == '[' && identifier[1] == '"') { /* " (dummy comment to avoid confusing some - * function lists in text editors) */ + if (identifier[0] == '[' && identifier[1] == '"') { /* id prop lookup, not so common */ PropertyRNA *r_prop = NULL; PointerRNA r_ptr; /* only support single level props */ @@ -967,19 +967,33 @@ const struct ListBase *RNA_struct_type_properties(StructRNA *srna) return &srna->cont.properties; } -PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier) +PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier) { return BLI_findstring_ptr(&srna->cont.properties, identifier, offsetof(PropertyRNA, identifier)); } +/** + * \note #RNA_struct_find_property is a higher level alternative to this function + * which takes a #PointerRNA instead of a #StructRNA. + */ +PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier) +{ + for (; srna; srna = srna->base) { + PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, identifier); + if (prop != NULL) { + return prop; + } + } + return NULL; +} + FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier) { #if 1 FunctionRNA *func; - StructRNA *type; - for (type = srna; type; type = type->base) { + for (; srna; srna = srna->base) { func = (FunctionRNA *)BLI_findstring_ptr( - &type->functions, identifier, offsetof(FunctionRNA, identifier)); + &srna->functions, identifier, offsetof(FunctionRNA, identifier)); if (func) { return func; } @@ -3002,7 +3016,7 @@ void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value) BLI_assert(RNA_property_type(prop) == PROP_FLOAT); BLI_assert(RNA_property_array_check(prop) == false); /* useful to check on bad values but set function should clamp */ - /* BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); */ + // BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); if ((idprop = rna_idproperty_check(&prop, ptr))) { RNA_property_float_clamp(ptr, prop, &value); @@ -3676,6 +3690,8 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; IDProperty *idprop; + static ThreadMutex lock = BLI_MUTEX_INITIALIZER; + BLI_assert(RNA_property_type(prop) == PROP_POINTER); if ((idprop = rna_idproperty_check(&prop, ptr))) { @@ -3695,9 +3711,14 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) return pprop->get(ptr); } if (prop->flag & PROP_IDPROPERTY) { - /* XXX temporary hack to add it automatically, reading should - * never do any write ops, to ensure thread safety etc. */ + /* NOTE: While creating/writing data in an accessor is really bad design-wise, this is + * currently very difficult to avoid in that case. So a global mutex is used to keep ensuring + * thread safety. */ + BLI_mutex_lock(&lock); + /* NOTE: We do not need to check again for existence of the pointer after locking here, since + * this is also done in #RNA_property_pointer_add itself. */ RNA_property_pointer_add(ptr, prop); + BLI_mutex_unlock(&lock); return RNA_property_pointer_get(ptr, prop); } return PointerRNA_NULL; @@ -4972,10 +4993,7 @@ void rna_iterator_array_end(CollectionPropertyIterator *iter) { ArrayIterator *internal = &iter->internal.array; - if (internal->free_ptr) { - MEM_freeN(internal->free_ptr); - internal->free_ptr = NULL; - } + MEM_SAFE_FREE(internal->free_ptr); } PointerRNA rna_array_lookup_int( @@ -6723,7 +6741,7 @@ bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool return RNA_property_is_set_ex(ptr, prop, use_ghost); } /* python raises an error */ - /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */ + // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); return 0; } @@ -6735,7 +6753,7 @@ bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier) return RNA_property_is_set(ptr, prop); } /* python raises an error */ - /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */ + // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); return 0; } diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 3912c873fd0..2c552970c82 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -1096,6 +1096,7 @@ static void rna_property_override_check_resync(Main *bmain, PointerRNA *ptr_item_dst, PointerRNA *ptr_item_src) { + ID *id_owner = rna_property_override_property_real_id_owner(bmain, ptr_dst, NULL, NULL); ID *id_src = rna_property_override_property_real_id_owner(bmain, ptr_item_src, NULL, NULL); ID *id_dst = rna_property_override_property_real_id_owner(bmain, ptr_item_dst, NULL, NULL); @@ -1109,9 +1110,18 @@ static void rna_property_override_check_resync(Main *bmain, * remapped to its new local override. In that case overrides and linked data * are always properly matching. */ id_src != id_dst && - /* If one of the pointers is NULL and not the other, or if linked reference ID - * of `id_src` is not `id_dst`, we are in a non-matching case. */ - (ELEM(NULL, id_src, id_dst) || id_src->override_library->reference != id_dst)) { + /* If one of the pointers is NULL and not the other, we are in a non-matching case. */ + (ELEM(NULL, id_src, id_dst) || + /* If `id_dst` is not from same lib as id_src, and linked reference ID of `id_src` is not + * `id_dst`, we are in a non-matching case. */ + (id_dst->lib != id_src->lib && id_src->override_library->reference != id_dst) || + /* If `id_dst` is from same lib as id_src, and is not same as `id_owner`, we are in a + * non-matching case. + * + * NOTE: Here we are testing if `id_owner` is referencing itself, in that case the new + * override copy generated by `BKE_lib_override_library_update` will already have its + * self-references updated to itself, instead of still pointing to its linked source. */ + (id_dst->lib == id_src->lib && id_dst != id_owner))) { ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; CLOG_INFO(&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name); } diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 49d02524e43..690506fa517 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -56,7 +56,7 @@ static void rna_Armature_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), DEG_id_tag_update(id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, id); - /*WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); */ + // WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); } static void rna_Armature_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -994,7 +994,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) } RNA_def_property_float_sdna(prop, NULL, "rad_head"); /* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */ - /*RNA_def_property_range(prop, 0, 1000); */ + // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); RNA_def_property_ui_text( prop, "Envelope Head Radius", "Radius of head of bone (for Envelope deform only)"); @@ -1008,7 +1008,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) } RNA_def_property_float_sdna(prop, NULL, "rad_tail"); /* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */ - /*RNA_def_property_range(prop, 0, 1000); */ + // RNA_def_property_range(prop, 0, 1000); RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3); RNA_def_property_ui_text( prop, "Envelope Tail Radius", "Radius of tail of bone (for Envelope deform only)"); @@ -1346,7 +1346,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) /* calculated and read only, not actual data access */ prop = RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX); - /* RNA_def_property_float_sdna(prop, NULL, ""); */ /* Doesn't access any real data. */ + // RNA_def_property_float_sdna(prop, NULL, ""); /* Doesn't access any real data. */ RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_THICK_WRAP); /* no reference to original data */ diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 80b2594d0c9..1e583f4ca52 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -273,7 +273,7 @@ static void rna_def_asset_handle_api(StructRNA *srna) parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, - "asset_library", + "asset_library_ref", "AssetLibraryReference", "", "The asset library containing the given asset, only valid if the asset " @@ -319,7 +319,7 @@ static void rna_def_asset_library_reference(BlenderRNA *brna) { StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL); RNA_def_struct_ui_text( - srna, "Asset Library Reference", "Identifier to refere to the asset library"); + srna, "Asset Library Reference", "Identifier to refer to the asset library"); } /** @@ -329,7 +329,7 @@ PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set) { - PropertyRNA *prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE); + PropertyRNA *prop = RNA_def_property(srna, "asset_library_ref", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, DummyRNA_NULL_items); RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf"); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 2b09ea51a84..cdca58df4b0 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1281,6 +1281,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static EnumPropertyItem rna_enum_gpencil_brush_caps_types_items[] = { + {GP_STROKE_CAP_ROUND, "ROUND", ICON_GP_CAPS_ROUND, "Round", ""}, + {GP_STROKE_CAP_FLAT, "FLAT", ICON_GP_CAPS_FLAT, "Flat", ""}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "BrushGpencilSettings", NULL); RNA_def_struct_sdna(srna, "BrushGpencilSettings"); RNA_def_struct_path_func(srna, "rna_BrushGpencilSettings_path"); @@ -1750,6 +1756,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_update( prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update"); + prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "caps_type"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_caps_types_items); + RNA_def_property_ui_text(prop, "Caps Type", "The shape of the start and end of the stroke"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "fill_draw_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "fill_draw_mode"); RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_draw_modes_items); diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index c25cea1b4b3..74d924b8937 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -37,6 +37,7 @@ # include "BKE_cachefile.h" # include "DEG_depsgraph.h" +# include "DEG_depsgraph_build.h" # include "WM_api.h" # include "WM_types.h" @@ -53,6 +54,12 @@ static void rna_CacheFile_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poin WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); } +static void rna_CacheFile_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_CacheFile_update(bmain, scene, ptr); + DEG_relations_tag_update(bmain); +} + static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { CacheFile *cache_file = (CacheFile *)ptr->data; @@ -64,8 +71,8 @@ static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, P /* cachefile.object_paths */ static void rna_def_alembic_object_path(BlenderRNA *brna) { - StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL); - RNA_def_struct_sdna(srna, "AlembicObjectPath"); + StructRNA *srna = RNA_def_struct(brna, "CacheObjectPath", NULL); + RNA_def_struct_sdna(srna, "CacheObjectPath"); RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive"); RNA_def_struct_ui_icon(srna, ICON_NONE); @@ -81,8 +88,8 @@ static void rna_def_alembic_object_path(BlenderRNA *brna) /* cachefile.object_paths */ static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop) { - RNA_def_property_srna(cprop, "AlembicObjectPaths"); - StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL); + RNA_def_property_srna(cprop, "CacheObjectPaths"); + StructRNA *srna = RNA_def_struct(brna, "CacheObjectPaths", NULL); RNA_def_struct_sdna(srna, "CacheFile"); RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths"); } @@ -105,6 +112,16 @@ static void rna_def_cachefile(BlenderRNA *brna) prop, "Sequence", "Whether the cache is separated in a series of files"); RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + prop = RNA_def_property(srna, "use_render_procedural", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Use Render Engine Procedural", + "Display boxes in the viewport as placeholders for the objects, Cycles will use a " + "procedural to load the objects during viewport rendering in experimental mode, " + "other render engines will also receive a placeholder and should take care of loading the " + "Alembic data themselves if possible"); + RNA_def_property_update(prop, 0, "rna_CacheFile_dependency_update"); + /* ----------------- For Scene time ------------------- */ prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE); @@ -133,6 +150,23 @@ static void rna_def_cachefile(BlenderRNA *brna) "determine which file to use in a file sequence"); RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Cache controls ----------------- */ + + prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Use Prefetch", + "When enabled, the Cycles Procedural will preload animation data for faster updates"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "prefetch_cache_size", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text( + prop, + "Prefetch Cache Size", + "Memory usage limit in megabytes for the Cycles Procedural cache, if the data does not " + "fit within the limit, rendering is aborted"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + /* ----------------- Axis Conversion ----------------- */ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); @@ -169,8 +203,8 @@ static void rna_def_cachefile(BlenderRNA *brna) NULL, NULL, NULL); - RNA_def_property_struct_type(prop, "AlembicObjectPath"); - RNA_def_property_srna(prop, "AlembicObjectPaths"); + RNA_def_property_struct_type(prop, "CacheObjectPath"); + RNA_def_property_srna(prop, "CacheObjectPaths"); RNA_def_property_ui_text( prop, "Object Paths", "Paths of the objects inside the Alembic archive"); diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c index 608a8e51b09..77276f47689 100644 --- a/source/blender/makesrna/intern/rna_collection.c +++ b/source/blender/makesrna/intern/rna_collection.c @@ -322,17 +322,17 @@ static void rna_Collection_flag_set(PointerRNA *ptr, const bool value, const int static void rna_Collection_hide_select_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_SELECT); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_SELECT); } static void rna_Collection_hide_viewport_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_VIEWPORT); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_VIEWPORT); } static void rna_Collection_hide_render_set(PointerRNA *ptr, bool value) { - rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_RENDER); + rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_RENDER); } static void rna_Collection_flag_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -496,7 +496,7 @@ void RNA_def_collections(BlenderRNA *brna) /* Flags */ prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_SELECT); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_SELECT); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_select_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); @@ -504,7 +504,7 @@ void RNA_def_collections(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update"); prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_VIEWPORT); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_VIEWPORT); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_viewport_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); @@ -512,7 +512,7 @@ void RNA_def_collections(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update"); prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_RENDER); + RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_RENDER); RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_render_set"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 3064703b02e..5968c8bac8f 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -679,7 +679,7 @@ static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value) acon->mix_mode = value; /* The After mode can be computed in world space for efficiency - * and backward compatibility, while Before requires Local. */ + * and backward compatibility, while Before or Split requires Local. */ if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) { con->ownspace = CONSTRAINT_SPACE_WORLD; } @@ -715,7 +715,7 @@ static int rna_SplineIKConstraint_joint_bindings_get_length(PointerRNA *ptr, length[0] = ikData->numpoints; } else { - length[0] = 256; /* for raw_access, untested */ + length[0] = 0; } return length[0]; @@ -1773,25 +1773,47 @@ static void rna_def_constraint_action(BlenderRNA *brna) }; static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_BEFORE_FULL, + "BEFORE_FULL", + 0, + "Before Original (Full)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, {ACTCON_MIX_BEFORE, "BEFORE", 0, - "Before Original", - "Apply the action channels before the original transformation, " - "as if applied to an imaginary parent with Aligned Inherit Scale"}, - {ACTCON_MIX_AFTER, - "AFTER", + "Before Original (Aligned)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_BEFORE_SPLIT, + "BEFORE_SPLIT", 0, - "After Original", - "Apply the action channels after the original transformation, " - "as if applied to an imaginary child with Aligned Inherit Scale"}, + "Before Original (Split Channels)", + "Apply the action channels before the original transformation, handling location, rotation " + "and scale separately"}, + {0, "", 0, NULL, NULL}, {ACTCON_MIX_AFTER_FULL, "AFTER_FULL", 0, - "After Original (Full Scale)", - "Apply the action channels after the original transformation, as if " - "applied to an imaginary child with Full Inherit Scale. This mode " - "can create shear and is provided only for backward compatibility"}, + "After Original (Full)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, + {ACTCON_MIX_AFTER, + "AFTER", + 0, + "After Original (Aligned)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_AFTER_SPLIT, + "AFTER_SPLIT", + 0, + "After Original (Split Channels)", + "Apply the action channels after the original transformation, handling location, rotation " + "and scale separately"}, {0, NULL, 0, NULL, NULL}, }; @@ -3487,6 +3509,12 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); + prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_OFF); + RNA_def_property_ui_text(prop, "Enabled", "Use the results of this constraint"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_ui_icon(prop, ICON_HIDE_ON, 1); + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c index b3ab8cc15a2..66c0fc72c7a 100644 --- a/source/blender/makesrna/intern/rna_curveprofile.c +++ b/source/blender/makesrna/intern/rna_curveprofile.c @@ -23,9 +23,6 @@ #include "DNA_curve_types.h" #include "DNA_curveprofile_types.h" -#include "DNA_texture_types.h" - -#include "BLI_utildefines.h" #include "RNA_define.h" #include "rna_internal.h" @@ -37,31 +34,7 @@ # include "RNA_access.h" -# include "DNA_image_types.h" -# include "DNA_material_types.h" -# include "DNA_movieclip_types.h" -# include "DNA_node_types.h" -# include "DNA_object_types.h" -# include "DNA_particle_types.h" -# include "DNA_sequence_types.h" - -# include "MEM_guardedalloc.h" - -# include "BKE_colorband.h" # include "BKE_curveprofile.h" -# include "BKE_image.h" -# include "BKE_linestyle.h" -# include "BKE_movieclip.h" -# include "BKE_node.h" - -# include "DEG_depsgraph.h" - -# include "ED_node.h" - -# include "IMB_colormanagement.h" -# include "IMB_imbuf.h" - -# include "SEQ_sequencer.h" /** * Set both handle types for all selected points in the profile-- faster than changing types diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 1f187382980..a38bbd3d6d2 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -809,7 +809,7 @@ static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr, length[0] = gen->arraysize; } else { - length[0] = 100; /* for raw_access, untested */ + length[0] = 0; } return length[0]; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index aad6f1231dd..3e5dce64c7b 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -2117,7 +2117,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER); RNA_def_property_ui_text( - prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer"); + prop, "Use Masks in Render", "Include the mask layers when rendering the view-layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* blend mode */ diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index c058ab6cfcc..e44ddb07d53 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -290,15 +290,10 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value) ImageTile *tile = (ImageTile *)ptr->data; Image *image = (Image *)ptr->owner_id; - /* The index of the first tile can't be changed. */ - if (tile->tile_number == 1001) { - return; - } - /* Check that no other tile already has that number. */ ImageTile *cur_tile = BKE_image_get_tile(image, value); - if (cur_tile == NULL || cur_tile == tile) { - tile->tile_number = value; + if (cur_tile == NULL) { + BKE_image_reassign_tile(image, tile, value); } } diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 0414afe1514..ab4cbc429ce 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -134,7 +134,7 @@ static bool rna_LayerCollection_visible_get(LayerCollection *layer_collection, b } if (v3d->local_collections_uuid & layer_collection->local_collections_bits) { - return (layer_collection->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0; + return (layer_collection->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0; } return false; diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c index 0593db0dd56..8bec337885e 100644 --- a/source/blender/makesrna/intern/rna_light.c +++ b/source/blender/makesrna/intern/rna_light.c @@ -188,6 +188,7 @@ static void rna_def_light(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based lights"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index ca97f2c9a55..8cd1ac0d963 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -2189,6 +2189,7 @@ static void rna_def_linestyle(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based shaders"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index 8c7d9698a67..9c90c209389 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -991,19 +991,19 @@ static void rna_def_mask_layer(BlenderRNA *brna) /* restrict */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_VIEW); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_VIEW); RNA_def_property_ui_text(prop, "Restrict View", "Restrict visibility in the viewport"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL); prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_SELECT); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_SELECT); RNA_def_property_ui_text(prop, "Restrict Select", "Restrict selection in the viewport"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL); prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_RENDER); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_RENDER); RNA_def_property_ui_text(prop, "Restrict Render", "Restrict renderability"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index d91c0bfaf29..144950235c8 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -849,8 +849,7 @@ void RNA_def_material(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); - /* XXX: remove once overrides in material node trees are supported. */ - RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 9caff88a3a5..fbc578acb8e 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1687,7 +1687,7 @@ static void rna_def_mvert(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION); - /* RNA_def_property_float_sdna(prop, NULL, "no"); */ + // RNA_def_property_float_sdna(prop, NULL, "no"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, -1.0f, 1.0f); RNA_def_property_float_funcs( diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index b424a575094..486d8d13564 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -595,6 +595,44 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[] = { + {SUBSURF_UV_SMOOTH_NONE, "NONE", 0, "None", "UVs are not smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, + "PRESERVE_CORNERS", + 0, + "Keep Corners", + "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, + "PRESERVE_CORNERS_AND_JUNCTIONS", + 0, + "Keep Corners, Junctions", + "UVs are smoothed, corners on discontinuous boundary and " + "junctions of 3 or more regions are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, + "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", + 0, + "Keep Corners, Junctions, Concave", + "UVs are smoothed, corners on discontinuous boundary, " + "junctions of 3 or more regions and darts and concave corners are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, + "PRESERVE_BOUNDARIES", + 0, + "Keep Boundaries", + "UVs are smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = { + {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS, + "PRESERVE_CORNERS", + 0, + "Keep Corners", + "Smooth boundaries, but corners are kept sharp"}, + {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DNA_curve_types.h" # include "DNA_fluid_types.h" @@ -1631,55 +1669,12 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) static void rna_def_property_subdivision_common(StructRNA *srna) { - static const EnumPropertyItem prop_uv_smooth_items[] = { - {SUBSURF_UV_SMOOTH_NONE, - "NONE", - 0, - "None", - "UVs are not smoothed, boundaries are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, - "PRESERVE_CORNERS", - 0, - "Keep Corners", - "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, - "PRESERVE_CORNERS_AND_JUNCTIONS", - 0, - "Keep Corners, Junctions", - "UVs are smoothed, corners on discontinuous boundary and " - "junctions of 3 or more regions are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, - "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", - 0, - "Keep Corners, Junctions, Concave", - "UVs are smoothed, corners on discontinuous boundary, " - "junctions of 3 or more regions and darts and concave corners are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, - "PRESERVE_BOUNDARIES", - 0, - "Keep Boundaries", - "UVs are smoothed, boundaries are kept sharp"}, - {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem prop_boundary_smooth_items[] = { - {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS, - "PRESERVE_CORNERS", - 0, - "Keep Corners", - "Smooth boundaries, but corners are kept sharp"}, - {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"}, - {0, NULL, 0, NULL, NULL}, - }; - PropertyRNA *prop; - RNA_define_lib_overridable(true); prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "uv_smooth"); - RNA_def_property_enum_items(prop, prop_uv_smooth_items); + RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items); RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -1693,7 +1688,7 @@ static void rna_def_property_subdivision_common(StructRNA *srna) prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth"); - RNA_def_property_enum_items(prop, prop_boundary_smooth_items); + RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items); RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3d4256db335..d8ab7c7a61b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -439,6 +439,34 @@ static const EnumPropertyItem rna_node_geometry_attribute_randomize_operation_it {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_curve_handle_type_items[] = { + {GEO_NODE_CURVE_HANDLE_FREE, + "FREE", + ICON_HANDLE_FREE, + "Free", + "The handle can be moved anywhere, and doesn't influence the point's other handle"}, + {GEO_NODE_CURVE_HANDLE_AUTO, + "AUTO", + ICON_HANDLE_AUTO, + "Auto", + "The location is automatically calculated to be smooth"}, + {GEO_NODE_CURVE_HANDLE_VECTOR, + "VECTOR", + ICON_HANDLE_VECTOR, + "Vector", + "The location is calculated to point to the next/previous control point"}, + {GEO_NODE_CURVE_HANDLE_ALIGN, + "ALIGN", + ICON_HANDLE_ALIGNED, + "Align", + "The location is constrained to point in the opposite direction as the other handle"}, + {0, NULL, 0, NULL, NULL}}; + +static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = { + {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Use the left handles"}, + {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"}, + {0, NULL, 0, NULL, NULL}}; + #ifndef RNA_RUNTIME static const EnumPropertyItem node_sampler_type_items[] = { {0, "NEAREST", 0, "Nearest", ""}, @@ -1008,7 +1036,7 @@ static void rna_NodeTree_get_from_context( void *ret1, *ret2, *ret3; RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */ - /* RNA_struct_find_function(&ptr, "get_from_context"); */ + // RNA_struct_find_function(&ptr, "get_from_context"); func = &rna_NodeTree_get_from_context_func; RNA_parameter_list_create(&list, &ptr, func); @@ -2959,7 +2987,7 @@ static void rna_NodeSocketInterface_register_properties(bNodeTree *ntree, } RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); - /* RNA_struct_find_function(&ptr, "register_properties"); */ + // RNA_struct_find_function(&ptr, "register_properties"); func = &rna_NodeSocketInterface_register_properties_func; RNA_parameter_list_create(&list, &ptr, func); @@ -2985,7 +3013,7 @@ static void rna_NodeSocketInterface_init_socket( RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr); - /* RNA_struct_find_function(&ptr, "init_socket"); */ + // RNA_struct_find_function(&ptr, "init_socket"); func = &rna_NodeSocketInterface_init_socket_func; RNA_parameter_list_create(&list, &ptr, func); @@ -3015,7 +3043,7 @@ static void rna_NodeSocketInterface_from_socket(bNodeTree *ntree, RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr); RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr); - /* RNA_struct_find_function(&ptr, "from_socket"); */ + // RNA_struct_find_function(&ptr, "from_socket"); func = &rna_NodeSocketInterface_from_socket_func; RNA_parameter_list_create(&list, &ptr, func); @@ -4717,6 +4745,7 @@ static void def_frame(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Text", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -5724,6 +5753,7 @@ static void def_sh_tex_pointdensity(StructRNA *srna) NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE); @@ -6143,6 +6173,7 @@ static void def_sh_tex_ies(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "IES Text", "Internal IES file"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -6183,6 +6214,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Text"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Script", "Internal shader script to define the shader"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeScript_update"); @@ -7908,6 +7940,7 @@ static void def_cmp_movieclip(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -7922,6 +7955,7 @@ static void def_cmp_stabilize2d(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -7952,6 +7986,7 @@ static void def_cmp_moviedistortion(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -7981,6 +8016,7 @@ static void def_cmp_mask(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Mask"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Mask", ""); prop = RNA_def_property(srna, "use_feather", PROP_BOOLEAN, PROP_NONE); @@ -8473,6 +8509,7 @@ static void def_cmp_keyingscreen(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8610,6 +8647,7 @@ static void def_cmp_trackpos(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8674,6 +8712,7 @@ static void def_cmp_planetrackdeform(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "MovieClip"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Movie Clip", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8804,6 +8843,7 @@ static void def_cmp_cryptomatte(StructRNA *srna) prop, "rna_NodeCryptomatte_scene_get", "rna_NodeCryptomatte_scene_set", NULL, NULL); RNA_def_property_struct_type(prop, "Scene"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Scene", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8815,6 +8855,7 @@ static void def_cmp_cryptomatte(StructRNA *srna) "rna_NodeCryptomatte_image_poll"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8914,6 +8955,7 @@ static void def_tex_image(StructRNA *srna) RNA_def_property_pointer_sdna(prop, NULL, "id"); RNA_def_property_struct_type(prop, "Image"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -9067,6 +9109,26 @@ static void def_geo_triangulate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_subdivision_surface(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySubdivisionSurface", "storage"); + prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "uv_smooth"); + RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items); + RNA_def_property_enum_default(prop, SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES); + RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth"); + RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items); + RNA_def_property_enum_default(prop, SUBSURF_BOUNDARY_SMOOTH_ALL); + RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_attribute_randomize(StructRNA *srna) { PropertyRNA *prop; @@ -9441,52 +9503,59 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_curve_set_handles(StructRNA *srna) +static void def_geo_curve_spline_type(StructRNA *srna) { static const EnumPropertyItem type_items[] = { - {GEO_NODE_CURVE_HANDLE_FREE, - "FREE", - ICON_HANDLE_FREE, - "Free", - "The handle can be moved anywhere, and doesn't influence the point's other handle"}, - {GEO_NODE_CURVE_HANDLE_AUTO, - "AUTO", - ICON_HANDLE_AUTO, - "Auto", - "The location is automatically calculated to be smooth"}, - {GEO_NODE_CURVE_HANDLE_VECTOR, - "VECTOR", - ICON_HANDLE_VECTOR, - "Vector", - "The location is calculated to point to the next/previous control point"}, - {GEO_NODE_CURVE_HANDLE_ALIGN, - "ALIGN", - ICON_HANDLE_ALIGNED, - "Align", - "The location is constrained to point in the opposite direction as the other handle"}, + {GEO_NODE_SPLINE_TYPE_BEZIER, "BEZIER", ICON_NONE, "Bezier", "Set the splines to Bezier"}, + {GEO_NODE_SPLINE_TYPE_NURBS, "NURBS", ICON_NONE, "NURBS", "Set the splines to NURBS"}, + {GEO_NODE_SPLINE_TYPE_POLY, "POLY", ICON_NONE, "Poly", "Set the splines to Poly"}, {0, NULL, 0, NULL, NULL}}; - static const EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Update the left handles"}, - {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Update the right handles"}, - {0, NULL, 0, NULL, NULL}}; + PropertyRNA *prop; + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSplineType", "storage"); + + prop = RNA_def_property(srna, "spline_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "spline_type"); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} +static void def_geo_curve_set_handles(StructRNA *srna) +{ PropertyRNA *prop; RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "handle_type"); - RNA_def_property_enum_items(prop, type_items); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); RNA_def_property_flag(prop, PROP_ENUM_FLAG); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_select_handles(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_primitive_circle(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { @@ -10874,6 +10943,7 @@ static void rna_def_node_socket_object(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -10909,6 +10979,7 @@ static void rna_def_node_socket_image(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -10959,6 +11030,7 @@ static void rna_def_node_socket_collection(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -10994,6 +11066,7 @@ static void rna_def_node_socket_texture(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11031,6 +11104,7 @@ static void rna_def_node_socket_material(BlenderRNA *brna, RNA_def_property_update( prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* socket interface */ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); @@ -11416,12 +11490,14 @@ static void rna_def_node(BlenderRNA *brna) prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "inputs", NULL); RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Inputs", ""); rna_def_node_sockets_api(brna, prop, SOCK_IN); prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "outputs", NULL); RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Outputs", ""); rna_def_node_sockets_api(brna, prop, SOCK_OUT); @@ -11868,6 +11944,7 @@ static void rna_def_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "nodes", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "nodes", NULL); RNA_def_property_struct_type(prop, "Node"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Nodes", ""); rna_def_nodetree_nodes_api(brna, prop); @@ -11885,6 +11962,7 @@ static void rna_def_nodetree(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block"); RNA_def_property_update(prop, NC_NODE, NULL); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ed681291e29..0f6b89722a4 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -218,6 +218,12 @@ static EnumPropertyItem instance_items_pointcloud[] = { {OB_DUPLIVERTS, "POINTS", 0, "Points", "Instantiate child objects on all points"}, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_empty[] = { + {0, "NONE", 0, "None", ""}, + INSTANCE_ITEM_COLLECTION, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -751,7 +757,7 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C const EnumPropertyItem *item; if (ob->type == OB_EMPTY) { - item = instance_items; + item = instance_items_empty; } else if (ob->type == OB_POINTCLOUD) { item = instance_items_pointcloud; @@ -2927,6 +2933,97 @@ static void rna_def_object_lineart(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_object_lineart_update"); } +static void rna_def_object_visibility(StructRNA *srna) +{ + PropertyRNA *prop; + + /* Hide options. */ + prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_VIEWPORT); + RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_SELECT); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_RENDER); + RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + /* Instancer options. */ + prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER); + RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering"); + RNA_def_property_update( + prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + + prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT); + RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport"); + RNA_def_property_update( + prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + + /* Ray visibility. */ + prop = RNA_def_property(srna, "visible_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_CAMERA); + RNA_def_property_ui_text(prop, "Camera Visibility", "Object visibility to camera rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_diffuse", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_DIFFUSE); + RNA_def_property_ui_text(prop, "Diffuse Visibility", "Object visibility to diffuse rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_glossy", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_GLOSSY); + RNA_def_property_ui_text(prop, "Glossy Visibility", "Object visibility to glossy rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_transmission", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_TRANSMISSION); + RNA_def_property_ui_text( + prop, "Transmission Visibility", "Object visibility to transmission rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_volume_scatter", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_VOLUME_SCATTER); + RNA_def_property_ui_text( + prop, "Volume Scatter Visibility", "Object visibility to volume scattering rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + prop = RNA_def_property(srna, "visible_shadow", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_SHADOW); + RNA_def_property_ui_text(prop, "Shadow Visibility", "Object visibility to shadow rays"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); + + /* Holdout and shadow catcher. */ + prop = RNA_def_property(srna, "is_holdout", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HOLDOUT); + RNA_def_property_ui_text( + prop, + "Holdout", + "Render objects as a holdout or matte, creating a hole in the image with zero alpha, to " + "fill out in compositing with real footage or another render"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); + + prop = RNA_def_property(srna, "is_shadow_catcher", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_SHADOW_CATCHER); + RNA_def_property_ui_text( + prop, + "Shadow Catcher", + "Only render shadows and reflections on this object, for compositing renders into real " + "footage. Objects with this setting are considered to already exist in the footage, " + "objects without it are synthetic objects being composited into it"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw"); +} + static void rna_def_object(BlenderRNA *brna) { StructRNA *srna; @@ -3506,37 +3603,7 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_struct_type(prop, "RigidBodyConstraint"); RNA_def_property_ui_text(prop, "Rigid Body Constraint", "Constraint constraining rigid bodies"); - /* restrict */ - prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_VIEWPORT); - RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_SELECT); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_RENDER); - RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders"); - RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update"); - - prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER); - RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering"); - RNA_def_property_update( - prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); - - prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT); - RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport"); - RNA_def_property_update( - prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update"); + rna_def_object_visibility(srna); /* instancing */ prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index de4cfb2b61a..f732e14d905 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1851,20 +1851,20 @@ static void rna_def_particle(BlenderRNA *brna) prop = RNA_def_property(srna, "birth_time", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "time"); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Birth Time", ""); prop = RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_TIME); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Lifetime", ""); prop = RNA_def_property(srna, "die_time", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "dietime"); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Die Time", ""); prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE); - /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */ + // RNA_def_property_range(prop, lowerLimitf, upperLimitf); RNA_def_property_ui_text(prop, "Size", ""); /* */ @@ -3658,7 +3658,7 @@ static void rna_def_particle_system(BlenderRNA *brna) /* access to particle settings is redirected through functions */ /* to allow proper id-buttons functionality */ prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE); - /*RNA_def_property_pointer_sdna(prop, NULL, "part"); */ + // RNA_def_property_pointer_sdna(prop, NULL, "part"); RNA_def_property_struct_type(prop, "ParticleSettings"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); RNA_def_property_pointer_funcs( diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 73924c45d52..4400d198b4a 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -896,6 +896,12 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_ui_text(prop, "Use Stereo Viewport", "Support rendering stereo 3D viewport"); + prop = RNA_def_property(srna, "bl_use_alembic_procedural", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_ALEMBIC_PROCEDURAL); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, "Use Alembic Procedural", "Support loading Alembic data at render time"); + RNA_define_verify_sdna(1); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 7d7eec6f256..9d158761a21 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -616,6 +616,7 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = { # include "BLI_string_utils.h" # include "DNA_anim_types.h" +# include "DNA_cachefile_types.h" # include "DNA_color_types.h" # include "DNA_mesh_types.h" # include "DNA_node_types.h" @@ -1619,6 +1620,11 @@ static void rna_RenderSettings_engine_update(Main *bmain, ED_render_engine_changed(bmain, true); } +static void rna_Scene_update_render_engine(Main *bmain) +{ + ED_render_engine_changed(bmain, true); +} + static bool rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr)) { return (BLI_listbase_count(&R_engines) > 1); @@ -7664,6 +7670,7 @@ void RNA_def_scene(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Compositing node tree"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); @@ -7835,6 +7842,10 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, NULL); RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update"); + func = RNA_def_function(srna, "update_render_engine", "rna_Scene_update_render_engine"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_MAIN); + RNA_def_function_ui_description(func, "Trigger a render engine update"); + /* Statistics */ func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get"); RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); @@ -7895,6 +7906,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "master_collection"); RNA_def_property_struct_type(prop, "Collection"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Collection", "Scene root collection that owns all the objects and other collections " diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 74fe2a26505..dad77b4aad5 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -148,9 +148,9 @@ static void rna_Sequence_invalidate_preprocessed_update(Main *UNUSED(bmain), } } -static void rna_Sequence_invalidate_composite_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) +static void UNUSED_FUNCTION(rna_Sequence_invalidate_composite_update)(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; Editing *ed = SEQ_editing_get(scene, false); @@ -1900,7 +1900,7 @@ static void rna_def_sequence(BlenderRNA *brna) /* stupid 0-100 -> 0-1 */ RNA_def_property_float_funcs(prop, "rna_Sequence_opacity_get", "rna_Sequence_opacity_set", NULL); RNA_def_property_update( - prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_composite_update"); + prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); prop = RNA_def_property(srna, "effect_fader", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 4895ab11618..264ccccd350 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -310,7 +310,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data); + double video_start_offset; + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -359,7 +360,7 @@ static Sequence *rna_Sequences_new_sound(ID *id, SeqLoadData load_data; SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.allow_invalid_file = true; - Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data, 0.0f); if (seq == NULL) { BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file"); diff --git a/source/blender/makesrna/intern/rna_simulation.c b/source/blender/makesrna/intern/rna_simulation.c index cc9a4bec2e7..6f5041c9ed1 100644 --- a/source/blender/makesrna/intern/rna_simulation.c +++ b/source/blender/makesrna/intern/rna_simulation.c @@ -43,6 +43,7 @@ static void rna_def_simulation(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree defining the simulation"); /* common */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1d4318602c2..5ab13e7b44e 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -778,6 +778,19 @@ static void rna_Space_show_region_toolbar_update(bContext *C, PointerRNA *ptr) rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOLS, RGN_FLAG_HIDDEN); } +static bool rna_Space_show_region_tool_props_get(PointerRNA *ptr) +{ + return !rna_Space_bool_from_region_flag_get_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN); +} +static void rna_Space_show_region_tool_props_set(PointerRNA *ptr, bool value) +{ + rna_Space_bool_from_region_flag_set_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN, !value); +} +static void rna_Space_show_region_tool_props_update(bContext *C, PointerRNA *ptr) +{ + rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN); +} + /* Channels Region. */ static bool rna_Space_show_region_channels_get(PointerRNA *ptr) { @@ -2569,13 +2582,13 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) /* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */ BLI_assert(ptr->type == &RNA_FileAssetSelectParams); - return ED_asset_library_reference_to_enum_value(¶ms->asset_library); + return ED_asset_library_reference_to_enum_value(¶ms->asset_library_ref); } static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value) { FileAssetSelectParams *params = ptr->data; - params->asset_library = ED_asset_library_reference_from_enum_value(value); + params->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) @@ -3196,6 +3209,10 @@ static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int regio region_type_mask &= ~(1 << RGN_TYPE_TOOLS); DEF_SHOW_REGION_PROPERTY(show_region_toolbar, "Toolbar", ""); } + if (region_type_mask & (1 << RGN_TYPE_TOOL_PROPS)) { + region_type_mask &= ~(1 << RGN_TYPE_TOOL_PROPS); + DEF_SHOW_REGION_PROPERTY(show_region_tool_props, "Toolbar", ""); + } if (region_type_mask & (1 << RGN_TYPE_CHANNELS)) { region_type_mask &= ~(1 << RGN_TYPE_CHANNELS); DEF_SHOW_REGION_PROPERTY(show_region_channels, "Channels", ""); @@ -4346,6 +4363,21 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_float_default(prop, 0.02); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "normals_constant_screen_size", PROP_FLOAT, PROP_PIXEL); + RNA_def_property_float_sdna(prop, NULL, "overlay.normals_constant_screen_size"); + RNA_def_property_ui_text(prop, "Normal Screen Size", "Screen size for normals in the 3D view"); + RNA_def_property_range(prop, 0.0, 100000.0); + RNA_def_property_ui_range(prop, 1.0, 100.0, 50, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_normals_constant_screen_size", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS); + RNA_def_property_ui_text(prop, + "Constant Screen Size Normals", + "Keep size of normals constant in relation to 3D view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "backwire_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "overlay.backwire_opacity"); RNA_def_property_ui_text(prop, "Backwire Opacity", "Opacity when rendering transparent wires"); @@ -6546,7 +6578,8 @@ static void rna_def_space_filebrowser(BlenderRNA *brna) RNA_def_struct_sdna(srna, "SpaceFile"); RNA_def_struct_ui_text(srna, "Space File Browser", "File browser space data"); - rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI)); + rna_def_space_generic_show_region_toggles( + srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_TOOL_PROPS)); prop = RNA_def_property(srna, "browse_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_space_file_browse_mode_items); @@ -7297,6 +7330,13 @@ static void rna_def_space_clip(BlenderRNA *brna) RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + /* transform */ + prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "cursor"); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); + /* pivot point */ prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "around"); diff --git a/source/blender/makesrna/intern/rna_speaker.c b/source/blender/makesrna/intern/rna_speaker.c index 43f0d27f514..2dce9d32006 100644 --- a/source/blender/makesrna/intern/rna_speaker.c +++ b/source/blender/makesrna/intern/rna_speaker.c @@ -55,7 +55,9 @@ static void rna_def_speaker(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Mute", "Mute the speaker"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "sound", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sound"); @@ -63,24 +65,30 @@ static void rna_def_speaker(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Sound", "Sound data-block used by this speaker"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume_max", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Maximum Volume", "Maximum volume, no matter how near the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume_min", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Minimum Volume", "Minimum volume, no matter how far away the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "distance_max", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -89,24 +97,30 @@ static void rna_def_speaker(BlenderRNA *brna) prop, "Maximum Distance", "Maximum distance for volume calculation, no matter how far away the object is"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "distance_reference", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Reference Distance", "Reference distance at which volume is 100%"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "attenuation", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text( prop, "Attenuation", "How strong the distance affects volume, depending on distance model"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_angle_outer", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -116,8 +130,10 @@ static void rna_def_speaker(BlenderRNA *brna) "Outer Cone Angle", "Angle of the outer cone, in degrees, outside this cone the volume is " "the outer cone volume, between inner and outer cone the volume is interpolated"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_angle_inner", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -126,29 +142,37 @@ static void rna_def_speaker(BlenderRNA *brna) prop, "Inner Cone Angle", "Angle of the inner cone, in degrees, inside the cone the volume is 100%"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "cone_volume_outer", PROP_FLOAT, PROP_FACTOR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Outer Cone Volume", "Volume outside the outer cone"); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "volume", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Volume", "How loud the sound is"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif prop = RNA_def_property(srna, "pitch", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.1f, 10.0f); RNA_def_property_ui_text(prop, "Pitch", "Playback pitch of the sound"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND); - /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL); */ - /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */ +# if 0 + RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL); + RNA_def_property_update(prop, 0, "rna_Speaker_update"); +# endif /* common */ rna_def_animdata_common(srna); diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c index 1351c027004..52f762e5494 100644 --- a/source/blender/makesrna/intern/rna_text.c +++ b/source/blender/makesrna/intern/rna_text.c @@ -240,8 +240,7 @@ static void rna_def_text(BlenderRNA *brna) prop = RNA_def_property(srna, "use_module", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", TXT_ISSCRIPT); - RNA_def_property_ui_text( - prop, "Register", "Run this text as a script on loading, Text name must end with \".py\""); + RNA_def_property_ui_text(prop, "Register", "Run this text as a Python script on loading"); prop = RNA_def_property(srna, "indentation", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flags"); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 128f1cb1e21..5a74cfa9964 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -1642,6 +1642,7 @@ static void rna_def_texture(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based textures"); RNA_def_property_update(prop, 0, "rna_Texture_nodes_update"); diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c index 42b3e4420c1..83c1efd55bc 100644 --- a/source/blender/makesrna/intern/rna_texture_api.c +++ b/source/blender/makesrna/intern/rna_texture_api.c @@ -59,14 +59,36 @@ void RNA_api_texture(StructRNA *srna) PropertyRNA *parm; func = RNA_def_function(srna, "evaluate", "texture_evaluate"); - RNA_def_function_ui_description(func, "Evaluate the texture at the coordinates given"); + RNA_def_function_ui_description( + func, "Evaluate the texture at the a given coordinate and returns the result"); - parm = RNA_def_float_vector(func, "value", 3, NULL, -FLT_MAX, FLT_MAX, "", "", -1e4, 1e4); + parm = RNA_def_float_vector( + func, + "value", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "The coordinates (x,y,z) of the texture, in case of a 3D texture, the z value is the slice " + "of the texture that is evaluated. For 2D textures such as images, the z value is ignored", + "", + -1e4, + 1e4); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); /* return location and normal */ parm = RNA_def_float_vector( - func, "result", 4, NULL, -FLT_MAX, FLT_MAX, "Result", NULL, -1e4, 1e4); + func, + "result", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "The result of the texture where (x,y,z,w) are (red, green, blue, intensity). For greyscale " + "textures, often intensity only will be used", + NULL, + -1e4, + 1e4); RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0); RNA_def_function_output(func, parm); } diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index a88b100435a..c506a533032 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1458,7 +1458,7 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Panel_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ @@ -1820,7 +1820,7 @@ static void rna_def_menu(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Menu_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index d29a90a1886..73811924c23 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1118,12 +1118,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem font_kerning_style[] = { - {0, "UNFITTED", 0, "Unfitted", "Use scaled but un-grid-fitted kerning distances"}, - {1, "FITTED", 0, "Fitted", "Use scaled and grid-fitted kerning distances"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "ThemeFontStyle", NULL); RNA_def_struct_sdna(srna, "uiFontStyle"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); @@ -1134,12 +1128,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Points", "Font size in points"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "font_kerning_style", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "kerning"); - RNA_def_property_enum_items(prop, font_kerning_style); - RNA_def_property_ui_text(prop, "Kerning Style", "Which style to use for font kerning"); - RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "shadow", PROP_INT, PROP_PIXEL); RNA_def_property_range(prop, 0, 5); RNA_def_property_ui_text(prop, "Shadow Size", "Shadow size (0, 3 and 5 supported)"); @@ -6155,7 +6143,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop, "Python Scripts Directory", "Alternate script path, matching the default layout with subdirectories: " - "startup, addons, modules, and presets (requires restart)"); + "startup, add-ons, modules, and presets (requires restart)"); /* TODO: editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); @@ -6297,12 +6285,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode"); - prop = RNA_def_property(srna, "use_asset_browser", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_asset_browser", 1); - RNA_def_property_ui_text( - prop, - "Asset Browser", - "Enable Asset Browser editor and operators to manage data-blocks as asset"); + prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, + "Extended Asset Browser", + "Enable Asset Browser editor and operators to manage regular " + "data-blocks as assets, not just poses"); + RNA_def_property_update(prop, 0, "rna_userdef_ui_update"); prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 667f3822935..b910648495b 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1915,7 +1915,7 @@ static void rna_def_operator(BlenderRNA *brna) /* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_def_struct_name_property(srna, prop); @@ -1923,7 +1923,7 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); @@ -1943,7 +1943,7 @@ static void rna_def_operator(BlenderRNA *brna) "rna_Operator_bl_description_get", "rna_Operator_bl_description_length", "rna_Operator_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); @@ -1953,7 +1953,7 @@ static void rna_def_operator(BlenderRNA *brna) "rna_Operator_bl_undo_group_get", "rna_Operator_bl_undo_group_length", "rna_Operator_bl_undo_group_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); @@ -2013,7 +2013,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->idname"); RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_def_struct_name_property(srna, prop); @@ -2021,7 +2021,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); @@ -2041,7 +2041,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) "rna_Operator_bl_description_get", "rna_Operator_bl_description_length", "rna_Operator_bl_description_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); @@ -2051,7 +2051,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) "rna_Operator_bl_undo_group_get", "rna_Operator_bl_undo_group_length", "rna_Operator_bl_undo_group_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); @@ -2073,11 +2073,13 @@ static void rna_def_operator_type_macro(BlenderRNA *brna) srna, "Operator Macro", "Storage of a sub operator in a macro after it has been added"); RNA_def_struct_sdna(srna, "wmOperatorTypeMacro"); - /* prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); */ - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ - /* RNA_def_property_string_sdna(prop, NULL, "idname"); */ - /* RNA_def_property_ui_text(prop, "Name", "Name of the sub operator"); */ - /* RNA_def_struct_name_property(srna, prop); */ +# if 0 + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_sdna(prop, NULL, "idname"); + RNA_def_property_ui_text(prop, "Name", "Name of the sub operator"); + RNA_def_struct_name_property(srna, prop); +# endif prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index bde15daa682..e123604cbe9 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -230,7 +230,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, bool repeat, bool head) { - /* wmWindowManager *wm = CTX_wm_manager(C); */ + // wmWindowManager *wm = CTX_wm_manager(C); wmKeyMapItem *kmi = NULL; char idname_bl[OP_MAX_TYPENAME]; int modifier = 0; diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 6a1574f3dbe..febb0e14e07 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -80,7 +80,7 @@ static void rna_gizmo_draw_cb(const struct bContext *C, struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "draw"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "draw")` directly. */ func = &rna_Gizmo_draw_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -98,7 +98,7 @@ static void rna_gizmo_draw_select_cb(const struct bContext *C, struct wmGizmo *g ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "draw_select"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "draw_select")` directly. */ func = &rna_Gizmo_draw_select_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -117,7 +117,7 @@ static int rna_gizmo_test_select_cb(struct bContext *C, struct wmGizmo *gz, cons ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "test_select"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "test_select")` directly. */ func = &rna_Gizmo_test_select_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -144,7 +144,7 @@ static int rna_gizmo_modal_cb(struct bContext *C, FunctionRNA *func; const int tweak_flag_int = tweak_flag; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "modal"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "modal")` directly. */ func = &rna_Gizmo_modal_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -168,7 +168,7 @@ static void rna_gizmo_setup_cb(struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "setup"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "setup")` directly. */ func = &rna_Gizmo_setup_func; RNA_parameter_list_create(&list, &gz_ptr, func); gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list); @@ -183,7 +183,7 @@ static int rna_gizmo_invoke_cb(struct bContext *C, struct wmGizmo *gz, const str ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "invoke"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "invoke")` directly. */ func = &rna_Gizmo_invoke_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -206,7 +206,7 @@ static void rna_gizmo_exit_cb(struct bContext *C, struct wmGizmo *gz, bool cance ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "exit"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "exit")` directly. */ func = &rna_Gizmo_exit_func; RNA_parameter_list_create(&list, &gz_ptr, func); RNA_parameter_set_lookup(&list, "context", &C); @@ -226,7 +226,7 @@ static void rna_gizmo_select_refresh_cb(struct wmGizmo *gz) ParameterList list; FunctionRNA *func; RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr); - /* RNA_struct_find_function(&gz_ptr, "select_refresh"); */ + /* Reference `RNA_struct_find_function(&gz_ptr, "select_refresh")` directly. */ func = &rna_Gizmo_select_refresh_func; RNA_parameter_list_create(&list, &gz_ptr, func); gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list); @@ -785,7 +785,7 @@ static void rna_gizmogroup_invoke_prepare_cb(const bContext *C, FunctionRNA *func; RNA_pointer_create(NULL, gzgroup->type->rna_ext.srna, gzgroup, &gzgroup_ptr); - /* RNA_struct_find_function(&wgroupr, "invoke_prepare"); */ + /* Reference `RNA_struct_find_function(&wgroupr, "invoke_prepare")` directly. */ func = &rna_GizmoGroup_invoke_prepare_func; RNA_parameter_list_create(&list, &gzgroup_ptr, func); @@ -1033,7 +1033,7 @@ static void rna_def_gizmo(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_string_sdna(prop, NULL, "type->idname"); RNA_def_property_string_maxlength(prop, MAX_NAME); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Gizmo_bl_idname_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); RNA_define_verify_sdna(1); /* not in sdna */ @@ -1362,7 +1362,7 @@ static void rna_def_gizmogroup(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->name"); RNA_def_property_string_maxlength(prop, MAX_NAME); /* else it uses the pointer size! */ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GizmoGroup_bl_label_set"); - /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER); prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 95f62d7de16..d76ad254140 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -112,13 +112,13 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace) static int rna_WorkSpace_asset_library_get(PointerRNA *ptr) { const WorkSpace *workspace = ptr->data; - return ED_asset_library_reference_to_enum_value(&workspace->asset_library); + return ED_asset_library_reference_to_enum_value(&workspace->asset_library_ref); } static void rna_WorkSpace_asset_library_set(PointerRNA *ptr, int value) { WorkSpace *workspace = ptr->data; - workspace->asset_library = ED_asset_library_reference_from_enum_value(value); + workspace->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace, diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index 1ca0eb74cf5..826e6d21c36 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -216,7 +216,7 @@ void RNA_def_world(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_float_array_default(prop, default_world_color); RNA_def_property_ui_text(prop, "Color", "Color of the background"); - /* RNA_def_property_update(prop, 0, "rna_World_update"); */ + // RNA_def_property_update(prop, 0, "rna_World_update"); /* render-only uses this */ RNA_def_property_update(prop, 0, "rna_World_draw_update"); @@ -237,6 +237,7 @@ void RNA_def_world(BlenderRNA *brna) prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "nodetree"); RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based worlds"); prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 56e8418972c..4cab92ad878 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -18,9 +18,14 @@ * \ingroup RNA */ +#include "BLI_math.h" + +#include "DNA_space_types.h" #include "DNA_view3d_types.h" +#include "DNA_windowmanager_types.h" #include "DNA_xr_types.h" +#include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -30,10 +35,452 @@ #ifdef RNA_RUNTIME -# include "BLI_math.h" +# include "BLI_listbase.h" # include "WM_api.h" +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_new(ami, name, replace_existing); +# else + UNUSED_VARS(ami, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_new_from_binding(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_add_copy(ami, amb_src); +# else + UNUSED_VARS(ami, amb_src); + return NULL; +# endif +} + +static void rna_XrActionMapBinding_remove(XrActionMapItem *ami, + ReportList *reports, + PointerRNA *amb_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = amb_ptr->data; + if (WM_xr_actionmap_binding_remove(ami, amb) == false) { + BKE_reportf(reports, + RPT_ERROR, + "ActionMapBinding '%s' cannot be removed from '%s'", + amb->name, + ami->name); + return; + } + RNA_POINTER_INVALIDATE(amb_ptr); +# else + UNUSED_VARS(ami, reports, amb_ptr); +# endif +} + +static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_binding_find(ami, name); +# else + UNUSED_VARS(ami, name); + return NULL; +# endif +} + +static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS0_POS) != 0) { + return XR_AXIS0_POS; + } + if ((amb->axis_flag & XR_AXIS0_NEG) != 0) { + return XR_AXIS0_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis0_region_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS0_POS | XR_AXIS0_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapBinding_axis1_region_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + if ((amb->axis_flag & XR_AXIS1_POS) != 0) { + return XR_AXIS1_POS; + } + if ((amb->axis_flag & XR_AXIS1_NEG) != 0) { + return XR_AXIS1_NEG; + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static void rna_XrActionMapBinding_axis1_region_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = ptr->data; + amb->axis_flag &= ~(XR_AXIS1_POS | XR_AXIS1_NEG); + amb->axis_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem); + if (ami) { + XrActionMapBinding *amb = ptr->data; + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am, + const char *name, + bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_new(am, name, replace_existing); +# else + UNUSED_VARS(am, name, replace_existing); + return NULL; +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_new_from_item(XrActionMap *am, + XrActionMapItem *ami_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_add_copy(am, ami_src); +# else + UNUSED_VARS(am, ami_src); + return NULL; +# endif +} + +static void rna_XrActionMapItem_remove(XrActionMap *am, ReportList *reports, PointerRNA *ami_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ami_ptr->data; + if (WM_xr_actionmap_item_remove(am, ami) == false) { + BKE_reportf( + reports, RPT_ERROR, "ActionMapItem '%s' cannot be removed from '%s'", ami->name, am->name); + return; + } + RNA_POINTER_INVALIDATE(ami_ptr); +# else + UNUSED_VARS(am, reports, ami_ptr); +# endif +} + +static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_item_find(am, name); +# else + UNUSED_VARS(am, name); + return NULL; +# endif +} + +static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + strcpy(value, WM_operatortype_name(ot, ami->op_properties_ptr)); + return; + } + } + strcpy(value, ami->op); + return; + } +# else + UNUSED_VARS(ptr); +# endif + value[0] = '\0'; +} + +static int rna_XrActionMapItem_op_name_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op[0]) { + if (ami->op_properties_ptr) { + wmOperatorType *ot = WM_operatortype_find(ami->op, 1); + if (ot) { + return strlen(WM_operatortype_name(ot, ami->op_properties_ptr)); + } + } + return strlen(ami->op); + } +# else + UNUSED_VARS(ptr); +# endif + return 0; +} + +static PointerRNA rna_XrActionMapItem_op_properties_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if (ami->op_properties_ptr) { + return *(ami->op_properties_ptr); + } +# else + UNUSED_VARS(ptr); +# endif + return PointerRNA_NULL; +} + +static bool rna_XrActionMapItem_bimanual_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->action_flag & XR_ACTION_BIMANUAL) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_bimanual_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->action_flag, value, XR_ACTION_BIMANUAL); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_haptic_match_user_paths_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_haptic_match_user_paths_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->haptic_flag, value, XR_HAPTIC_MATCHUSERPATHS); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrActionMapItem_haptic_mode_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->haptic_flag & XR_HAPTIC_RELEASE) != 0) { + return ((ami->haptic_flag & XR_HAPTIC_PRESS) != 0) ? (XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE) : + XR_HAPTIC_RELEASE; + } + if ((ami->haptic_flag & XR_HAPTIC_REPEAT) != 0) { + return XR_HAPTIC_REPEAT; + } +# else + UNUSED_VARS(ptr); +# endif + return XR_HAPTIC_PRESS; +} + +static void rna_XrActionMapItem_haptic_mode_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + ami->haptic_flag &= ~(XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE | XR_HAPTIC_REPEAT); + ami->haptic_flag |= value; +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_grip_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_GRIP) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_grip_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_GRIP); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrActionMapItem_pose_is_controller_aim_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + if ((ami->pose_flag & XR_POSE_AIM) != 0) { + return true; + } +# else + UNUSED_VARS(ptr); +# endif + return false; +} + +static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_AIM); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime); + short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime); + XrActionMap *actionmap = BLI_findlink(actionmaps, idx); + if (actionmap) { + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +static void rna_XrActionMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = ptr->data; + WM_xr_actionmap_item_properties_update_ot(ami); +# else + UNUSED_VARS(ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_new(wmXrData *xr, const char *name, bool replace_existing) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_new(xr->runtime, name, replace_existing); +# else + UNUSED_VARS(xr, name, replace_existing); + return NULL; +# endif +} + +static XrActionMap *rna_XrActionMap_new_from_actionmap(wmXrData *xr, XrActionMap *am_src) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_add_copy(xr->runtime, am_src); +# else + UNUSED_VARS(xr, am_src); + return NULL; +# endif +} + +static void rna_XrActionMap_remove(wmXrData *xr, ReportList *reports, PointerRNA *actionmap_ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = actionmap_ptr->data; + if (WM_xr_actionmap_remove(xr->runtime, actionmap) == false) { + BKE_reportf(reports, RPT_ERROR, "ActionMap '%s' cannot be removed", actionmap->name); + return; + } + RNA_POINTER_INVALIDATE(actionmap_ptr); +# else + UNUSED_VARS(xr, reports, actionmap_ptr); +# endif +} + +static XrActionMap *rna_XrActionMap_find(wmXrData *xr, const char *name) +{ +# ifdef WITH_XR_OPENXR + return WM_xr_actionmap_find(xr->runtime, name); +# else + UNUSED_VARS(xr, name); + return NULL; +# endif +} + +static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = bmain->wm.first; + if (wm && wm->xr.runtime) { + XrActionMap *actionmap = ptr->data; + WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap); + } +# else + UNUSED_VARS(bmain, ptr); +# endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + # ifdef WITH_XR_OPENXR static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) { @@ -49,6 +496,10 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) } # endif +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -91,6 +542,12 @@ static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, boo # endif } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + static bool rna_XrSessionState_is_running(bContext *C) { # ifdef WITH_XR_OPENXR @@ -112,6 +569,303 @@ static void rna_XrSessionState_reset_to_base_pose(bContext *C) # endif } +static bool rna_XrSessionState_action_set_create(bContext *C, XrActionMap *actionmap) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_action_set_create(&wm->xr, actionmap->name); +# else + UNUSED_VARS(C, actionmap); + return false; +# endif +} + +static bool rna_XrSessionState_action_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); + wmOperatorType *ot = NULL; + IDProperty *op_properties = NULL; + int64_t haptic_duration_msec; + + if (is_button_action) { + if (ami->op[0]) { + char idname[OP_MAX_TYPENAME]; + WM_operator_bl_idname(idname, ami->op); + ot = WM_operatortype_find(idname, true); + if (ot) { + op_properties = ami->op_properties; + } + } + + haptic_duration_msec = (int64_t)(ami->haptic_duration * 1000.0f); + } + + return WM_xr_action_create(&wm->xr, + actionmap->name, + ami->name, + ami->type, + count_subaction_paths, + subaction_paths, + ot, + op_properties, + is_button_action ? ami->haptic_name : NULL, + is_button_action ? &haptic_duration_msec : NULL, + is_button_action ? &ami->haptic_frequency : NULL, + is_button_action ? &ami->haptic_amplitude : NULL, + ami->op_flag, + ami->action_flag, + ami->haptic_flag); +# else + UNUSED_VARS(C, actionmap, ami); + return false; +# endif +} + +static bool rna_XrSessionState_action_binding_create(bContext *C, + XrActionMap *actionmap, + XrActionMapItem *ami, + XrActionMapBinding *amb) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + unsigned int count_subaction_paths = 0; + const char *subaction_paths[2]; + const char *component_paths[2]; + + if (ami->user_path0[0]) { + subaction_paths[0] = ami->user_path0; + component_paths[0] = amb->component_path0; + ++count_subaction_paths; + + if (ami->user_path1[0]) { + subaction_paths[1] = ami->user_path1; + component_paths[1] = amb->component_path1; + ++count_subaction_paths; + } + } + else { + if (ami->user_path1[0]) { + subaction_paths[0] = ami->user_path1; + component_paths[0] = amb->component_path1; + ++count_subaction_paths; + } + else { + return false; + } + } + + const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); + const bool is_pose_action = (ami->type == XR_POSE_INPUT); + float float_thresholds[2]; + eXrAxisFlag axis_flags[2]; + wmXrPose poses[2]; + + if (is_float_action) { + float_thresholds[0] = float_thresholds[1] = amb->float_threshold; + } + if (is_button_action) { + axis_flags[0] = axis_flags[1] = amb->axis_flag; + } + if (is_pose_action) { + copy_v3_v3(poses[0].position, amb->pose_location); + eul_to_quat(poses[0].orientation_quat, amb->pose_rotation); + normalize_qt(poses[0].orientation_quat); + memcpy(&poses[1], &poses[0], sizeof(poses[1])); + } + + return WM_xr_action_binding_create(&wm->xr, + actionmap->name, + ami->name, + amb->profile, + count_subaction_paths, + subaction_paths, + component_paths, + is_float_action ? float_thresholds : NULL, + is_button_action ? axis_flags : NULL, + is_pose_action ? poses : NULL); +# else + UNUSED_VARS(C, actionmap, ami, amb); + return false; +# endif +} + +bool rna_XrSessionState_active_action_set_set(bContext *C, const char *action_set_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_active_action_set_set(&wm->xr, action_set_name); +# else + UNUSED_VARS(C, action_set_name); + return false; +# endif +} + +bool rna_XrSessionState_controller_pose_actions_set(bContext *C, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_controller_pose_actions_set( + &wm->xr, action_set_name, grip_action_name, aim_action_name); +# else + UNUSED_VARS(C, action_set_name, grip_action_name, aim_action_name); + return false; +# endif +} + +void rna_XrSessionState_action_state_get(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float r_state[2]) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + wmXrActionState state; + if (WM_xr_action_state_get(&wm->xr, action_set_name, action_name, user_path, &state)) { + switch (state.type) { + case XR_BOOLEAN_INPUT: + r_state[0] = (float)state.state_boolean; + r_state[1] = 0.0f; + return; + case XR_FLOAT_INPUT: + r_state[0] = state.state_float; + r_state[1] = 0.0f; + return; + case XR_VECTOR2F_INPUT: + copy_v2_v2(r_state, state.state_vector2f); + return; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + } +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif + zero_v2(r_state); +} + +bool rna_XrSessionState_haptic_action_apply(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path, + float duration, + float frequency, + float amplitude) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + int64_t duration_msec = (int64_t)(duration * 1000.0f); + return WM_xr_haptic_action_apply(&wm->xr, + action_set_name, + action_name, + user_path[0] ? user_path : NULL, + &duration_msec, + &frequency, + &litude); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path, duration, frequency, amplitude); + return false; +# endif +} + +void rna_XrSessionState_haptic_action_stop(bContext *C, + const char *action_set_name, + const char *action_name, + const char *user_path) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_haptic_action_stop(&wm->xr, action_set_name, action_name, user_path[0] ? user_path : NULL); +# else + UNUSED_VARS(C, action_set_name, action_name, user_path); +# endif +} + +static void rna_XrSessionState_controller_grip_location_get(bContext *C, + int index, + float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_location_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_controller_grip_rotation_get(bContext *C, + int index, + float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_grip_rotation_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_controller_aim_location_get(bContext *C, + int index, + float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_location_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_controller_aim_rotation_get(bContext *C, + int index, + float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_state_controller_aim_rotation_get(&wm->xr, index, r_values); +# else + UNUSED_VARS(C, index); + unit_qt(r_values); +# endif +} + static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR @@ -134,8 +888,490 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * # endif } +static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + ListBase *lb = WM_xr_actionmaps_get(xr->runtime); + rna_iterator_listbase_begin(iter, lb, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return WM_xr_actionmap_active_index_get(xr->runtime); +# else + UNUSED_VARS(ptr); + return -1; +# endif +} + +static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_active_index_set(xr->runtime, (short)value); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return WM_xr_actionmap_selected_index_get(xr->runtime); +# else + UNUSED_VARS(ptr); + return -1; +# endif +} + +static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_actionmap_selected_index_set(xr->runtime, (short)value); +# else + UNUSED_VARS(ptr, value); +# endif +} + +/** \} */ + #else /* RNA_RUNTIME */ +/* -------------------------------------------------------------------- */ + +static const EnumPropertyItem rna_enum_xr_action_types[] = { + {XR_FLOAT_INPUT, + "FLOAT", + 0, + "Float", + "Float action, representing either a digital or analog button"}, + {XR_VECTOR2F_INPUT, + "VECTOR2D", + 0, + "Vector2D", + "2D float vector action, representing a thumbstick or trackpad"}, + {XR_POSE_INPUT, + "POSE", + 0, + "Pose", + "3D pose action, representing a controller's location and rotation"}, + {XR_VIBRATION_OUTPUT, + "VIBRATION", + 0, + "Vibration", + "Haptic vibration output action, to be applied with a duration, frequency, and amplitude"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_op_flags[] = { + {XR_OP_PRESS, + "PRESS", + 0, + "Press", + "Execute operator on button press (non-modal operators only)"}, + {XR_OP_RELEASE, + "RELEASE", + 0, + "Release", + "Execute operator on button release (non-modal operators only)"}, + {XR_OP_MODAL, "MODAL", 0, "Modal", "Use modal execution (modal operators only)"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_haptic_flags[] = { + {XR_HAPTIC_PRESS, "PRESS", 0, "Press", "Apply haptics on button press"}, + {XR_HAPTIC_RELEASE, "RELEASE", 0, "Release", "Apply haptics on button release"}, + {XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE, + "PRESS_RELEASE", + 0, + "Press Release", + "Apply haptics on button press and release"}, + {XR_HAPTIC_REPEAT, + "REPEAT", + 0, + "Repeat", + "Apply haptics repeatedly for the duration of the button press"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis0_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS0_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS0_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_xr_axis1_flags[] = { + {0, "ANY", 0, "Any", "Use any axis region for operator execution"}, + {XR_AXIS1_POS, + "POSITIVE", + 0, + "Positive", + "Use positive axis region only for operator execution"}, + {XR_AXIS1_NEG, + "NEGATIVE", + 0, + "Negative", + "Use negative axis region only for operator execution"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* -------------------------------------------------------------------- */ +/** \name XR Action Map + * \{ */ + +static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapBindings"); + srna = RNA_def_struct(brna, "XrActionMapBindings", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR Action Map Bindings", "Collection of XR action map bindings"); + + func = RNA_def_function(srna, "new", "rna_XrActionMapBinding_new"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map binding", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing binding with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_binding", "rna_XrActionMapBinding_new_from_binding"); + parm = RNA_def_pointer( + func, "binding", "XrActionMapBinding", "Binding", "Binding to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "result", "XrActionMapBinding", "Binding", "Added action map binding"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapBinding_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "binding", "XrActionMapBinding", "Binding", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapBinding_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "binding", + "XrActionMapBinding", + "Binding", + "The action map binding with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMapItems"); + srna = RNA_def_struct(brna, "XrActionMapItems", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + RNA_def_struct_ui_text(srna, "XR Action Map Items", "Collection of XR action map items"); + + func = RNA_def_function(srna, "new", "rna_XrActionMapItem_new"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map item", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing item with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_item", "rna_XrActionMapItem_new_from_item"); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Item to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMapItem", "Item", "Added action map item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMapItem_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMapItem_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "item", "XrActionMapItem", "Item", "The action map item with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrActionMaps"); + srna = RNA_def_struct(brna, "XrActionMaps", NULL); + RNA_def_struct_sdna(srna, "wmXrData"); + RNA_def_struct_ui_text(srna, "XR Action Maps", "Collection of XR action maps"); + + func = RNA_def_function(srna, "new", "rna_XrActionMap_new"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, + "replace_existing", + true, + "Replace Existing", + "Replace any existing actionmap with the same name"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_from_actionmap", "rna_XrActionMap_new_from_actionmap"); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "Action map to use as a reference"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "XrActionMap", "Action Map", "Added action map"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrActionMap_remove"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Removed action map"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrActionMap_find"); + parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "actionmap", "XrActionMap", "Action Map", "The action map with the given name"); + RNA_def_function_return(func, parm); +} + +static void rna_def_xr_actionmap(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* XrActionMap */ + srna = RNA_def_struct(brna, "XrActionMap", NULL); + RNA_def_struct_sdna(srna, "XrActionMap"); + RNA_def_struct_ui_text(srna, "XR Action Map", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map"); + RNA_def_property_update(prop, 0, "rna_XrActionMap_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "items", NULL); + RNA_def_property_struct_type(prop, "XrActionMapItem"); + RNA_def_property_ui_text( + prop, + "Items", + "Items in the action map, mapping an XR event to an operator, pose, or haptic output"); + rna_def_xr_actionmap_items(brna, prop); + + prop = RNA_def_property(srna, "selected_item", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selitem"); + RNA_def_property_ui_text(prop, "Selected Item", ""); + + /* XrActionMapItem */ + srna = RNA_def_struct(brna, "XrActionMapItem", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR Action Map Item", ""); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map item"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_action_types); + RNA_def_property_ui_text(prop, "Type", "Action type"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path"); + + prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 64); + RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path"); + + prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); + RNA_def_property_ui_text(prop, "Operator", "Identifier of operator to call on action event"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Operator Name", "Name of operator (translated) to call on action event"); + RNA_def_property_string_funcs( + prop, "rna_XrActionMapItem_op_name_get", "rna_XrActionMapItem_op_name_length", NULL); + + prop = RNA_def_property(srna, "op_properties", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "OperatorProperties"); + RNA_def_property_pointer_funcs(prop, "rna_XrActionMapItem_op_properties_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Operator Properties", "Properties to set when the operator is called"); + RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); + + prop = RNA_def_property(srna, "op_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "op_flag"); + RNA_def_property_enum_items(prop, rna_enum_xr_op_flags); + RNA_def_property_ui_text(prop, "Operator Mode", "Operator execution mode"); + + prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_XrActionMapItem_bimanual_get", "rna_XrActionMapItem_bimanual_set"); + RNA_def_property_ui_text( + prop, "Bimanual", "The action depends on the states/poses of both user paths"); + + prop = RNA_def_property(srna, "pose_is_controller_grip", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_grip_get", + "rna_XrActionMapItem_pose_is_controller_grip_set"); + RNA_def_property_ui_text( + prop, "Is Controller Grip", "The action poses will be used for the VR controller grips"); + + prop = RNA_def_property(srna, "pose_is_controller_aim", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_pose_is_controller_aim_get", + "rna_XrActionMapItem_pose_is_controller_aim_set"); + RNA_def_property_ui_text( + prop, "Is Controller Aim", "The action poses will be used for the VR controller aims"); + + prop = RNA_def_property(srna, "haptic_name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "Haptic Name", "Name of the haptic action to apply when executing this action"); + + prop = RNA_def_property(srna, "haptic_match_user_paths", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrActionMapItem_haptic_match_user_paths_get", + "rna_XrActionMapItem_haptic_match_user_paths_set"); + RNA_def_property_ui_text( + prop, + "Haptic Match User Paths", + "Apply haptics to the same user paths for the haptic action and this action"); + + prop = RNA_def_property(srna, "haptic_duration", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration"); + + prop = RNA_def_property(srna, "haptic_frequency", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_text(prop, + "Haptic Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency"); + + prop = RNA_def_property(srna, "haptic_amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text( + prop, "Haptic Amplitude", "Intensity of the haptic vibration, ranging from 0.0 to 1.0"); + + prop = RNA_def_property(srna, "haptic_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_haptic_flags); + RNA_def_property_enum_funcs( + prop, "rna_XrActionMapItem_haptic_mode_get", "rna_XrActionMapItem_haptic_mode_set", NULL); + RNA_def_property_ui_text(prop, "Haptic mode", "Haptic application mode"); + + prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrActionMapBinding"); + RNA_def_property_ui_text( + prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input"); + rna_def_xr_actionmap_bindings(brna, prop); + + prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "selbinding"); + RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); + + /* XrActionMapBinding */ + srna = RNA_def_struct(brna, "XrActionMapBinding", NULL); + RNA_def_struct_sdna(srna, "XrActionMapBinding"); + RNA_def_struct_ui_text(srna, "XR Action Map Binding", "Binding in an XR action map item"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the action map binding"); + RNA_def_property_update(prop, 0, "rna_XrActionMapBinding_name_update"); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "profile", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 256); + RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path"); + + prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path"); + + prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, 192); + RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path"); + + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "float_threshold"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Threshold", "Input threshold for button/axis actions"); + + prop = RNA_def_property(srna, "axis0_region", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_axis0_flags); + RNA_def_property_enum_funcs(prop, + "rna_XrActionMapBinding_axis0_region_get", + "rna_XrActionMapBinding_axis0_region_set", + NULL); + RNA_def_property_ui_text( + prop, "Axis 0 Region", "Action execution region for the first input axis"); + + prop = RNA_def_property(srna, "axis1_region", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_xr_axis1_flags); + RNA_def_property_enum_funcs(prop, + "rna_XrActionMapBinding_axis1_region_get", + "rna_XrActionMapBinding_axis1_region_set", + NULL); + RNA_def_property_ui_text( + prop, "Axis 1 Region", "Action execution region for the second input axis"); + + prop = RNA_def_property(srna, "pose_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_ui_text(prop, "Pose Location Offset", ""); + + prop = RNA_def_property(srna, "pose_rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_ui_text(prop, "Pose Rotation Offset", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Settings + * \{ */ + static void rna_def_xr_session_settings(BlenderRNA *brna) { StructRNA *srna; @@ -241,6 +1477,12 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session State + * \{ */ + static void rna_def_xr_session_state(BlenderRNA *brna) { StructRNA *srna; @@ -265,6 +1507,260 @@ static void rna_def_xr_session_state(BlenderRNA *brna) parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + func = RNA_def_function(srna, "action_set_create", "rna_XrSessionState_action_set_create"); + RNA_def_function_ui_description(func, "Create a VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_create", "rna_XrSessionState_action_create"); + RNA_def_function_ui_description(func, "Create a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "action_binding_create", "rna_XrSessionState_action_binding_create"); + RNA_def_function_ui_description(func, "Create a VR action binding"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "actionmap_binding", "XrActionMapBinding", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "active_action_set_set", "rna_XrSessionState_active_action_set_set"); + RNA_def_function_ui_description(func, "Set the active VR action set"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function( + srna, "controller_pose_actions_set", "rna_XrSessionState_controller_pose_actions_set"); + RNA_def_function_ui_description(func, "Set the actions that determine the VR controller poses"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "grip_action", + NULL, + 64, + "Grip Action", + "Name of the action representing the controller grips"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, + "aim_action", + NULL, + 64, + "Aim Action", + "Name of the action representing the controller aims"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "action_state_get", "rna_XrSessionState_action_state_get"); + RNA_def_function_ui_description(func, "Get the current state of a VR action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_array( + func, + "state", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Action State", + "Current state of the VR action. Second float value is only set for 2D vector type actions", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function(srna, "haptic_action_apply", "rna_XrSessionState_haptic_action_apply"); + RNA_def_function_ui_description(func, "Apply a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be applied to all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float(func, + "duration", + 0.0f, + 0.0f, + FLT_MAX, + "Duration", + "Haptic duration in seconds. 0.0 is the minimum supported duration", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "frequency", + 0.0f, + 0.0f, + FLT_MAX, + "Frequency", + "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR " + "runtime's default frequency", + 0.0f, + FLT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float(func, + "amplitude", + 1.0f, + 0.0f, + 1.0f, + "Amplitude", + "Haptic amplitude, ranging from 0.0 to 1.0", + 0.0f, + 1.0f); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "haptic_action_stop", "rna_XrSessionState_haptic_action_stop"); + RNA_def_function_ui_description(func, "Stop a VR haptic action"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, + "user_path", + NULL, + 64, + "User Path", + "Optional OpenXR user path. If not set, the action will be stopped for all paths"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function( + srna, "controller_grip_location_get", "rna_XrSessionState_controller_grip_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller grip location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller grip location", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_grip_rotation_get", "rna_XrSessionState_controller_grip_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller grip rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller grip quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_location_get", "rna_XrSessionState_controller_aim_location_get"); + RNA_def_function_ui_description(func, + "Get the last known controller aim location in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_translation(func, + "location", + 3, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Controller aim location", + -FLT_MAX, + FLT_MAX); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + + func = RNA_def_function( + srna, "controller_aim_rotation_get", "rna_XrSessionState_controller_aim_rotation_get"); + RNA_def_function_ui_description( + func, "Get the last known controller aim rotation (quaternion) in world space"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_float_vector(func, + "rotation", + 4, + NULL, + -FLT_MAX, + FLT_MAX, + "Rotation", + "Controller aim quaternion rotation", + -FLT_MAX, + FLT_MAX); + parm->subtype = PROP_QUATERNION; + RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT); + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 3); RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); @@ -282,12 +1778,43 @@ static void rna_def_xr_session_state(BlenderRNA *brna) prop, "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_XrSessionState_actionmaps_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_struct_type(prop, "XrActionMap"); + RNA_def_property_ui_text(prop, "XR Action Maps", ""); + rna_def_xr_actionmaps(brna, prop); + + prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_active_actionmap_get", + "rna_XrSessionState_active_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Active Action Map", ""); + + prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, + "rna_XrSessionState_selected_actionmap_get", + "rna_XrSessionState_selected_actionmap_set", + NULL); + RNA_def_property_ui_text(prop, "Selected Action Map", ""); } +/** \} */ + void RNA_def_xr(BlenderRNA *brna) { RNA_define_animate_sdna(false); + rna_def_xr_actionmap(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 0138dd0c3ad..d9b9fa96d04 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -141,6 +141,16 @@ if(WITH_ALEMBIC) ) endif() +if(WITH_USD) + add_definitions(-DWITH_USD) + list(APPEND INC + ../io/usd + ) + list(APPEND LIB + bf_usd + ) +endif() + if(WITH_MOD_REMESH) list(APPEND INC ../../../intern/dualcon diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 52f21e3d3d0..a344a15b0c1 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -61,7 +61,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BuildModifierData), modifier); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 4487adcfdda..fa2f70e1a9c 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -220,7 +220,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tclmd->solver_result = NULL; } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 5dd57469914..e7d5fe056c5 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -95,7 +95,9 @@ static void freeData(ModifierData *md) } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index a7ac9f618af..07da18f990d 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -95,7 +95,9 @@ static void requiredDataMask(Object *UNUSED(ob), } } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { DisplaceModifierData *dmd = (DisplaceModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 8b1d541d45d..77ae5c4b6f1 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -155,7 +155,9 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index bf197dca7e5..493b59b3a1a 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -86,7 +86,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla temd->facepa = NULL; } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 36d2ab2a11a..a14d582063a 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -132,7 +132,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * #endif /* WITH_FLUID */ } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 63495c4104e..a36a8c386b4 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -66,7 +66,7 @@ struct BLaplacianSystem { int numVerts; /* Number of verts. */ short *numNeFa; /* Number of neighbors faces around vertice. */ short *numNeEd; /* Number of neighbors Edges around vertice. */ - short *zerola; /* Is zero area or length. */ + bool *zerola; /* Is zero area or length. */ /* Pointers to data. */ float (*vertexCos)[3]; @@ -130,7 +130,7 @@ static void memset_laplacian_system(LaplacianSystem *sys, int val) memset(sys->ring_areas, val, sizeof(float) * sys->numVerts); memset(sys->vlengths, val, sizeof(float) * sys->numVerts); memset(sys->vweights, val, sizeof(float) * sys->numVerts); - memset(sys->zerola, val, sizeof(short) * sys->numVerts); + memset(sys->zerola, val, sizeof(bool) * sys->numVerts); } static LaplacianSystem *init_laplacian_system(int a_numEdges, @@ -152,7 +152,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, sys->ring_areas = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); sys->vlengths = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); sys->vweights = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__); - sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(short), __func__); + sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(bool), __func__); return sys; } @@ -225,8 +225,8 @@ static void init_laplacian_matrix(LaplacianSystem *sys) sys->numNeEd[idv2] = sys->numNeEd[idv2] + 1; w1 = len_v3v3(v1, v2); if (w1 < sys->min_area) { - sys->zerola[idv1] = 1; - sys->zerola[idv2] = 1; + sys->zerola[idv1] = true; + sys->zerola[idv2] = true; } else { w1 = 1.0f / w1; @@ -253,7 +253,7 @@ static void init_laplacian_matrix(LaplacianSystem *sys) areaf = area_tri_v3(v_prev, v_curr, v_next); if (areaf < sys->min_area) { - sys->zerola[l_curr->v] = 1; + sys->zerola[l_curr->v] = true; } sys->ring_areas[l_prev->v] += areaf; @@ -300,7 +300,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) const uint l_curr_index = l_curr - sys->mloop; /* Is ring if number of faces == number of edges around vertice. */ - if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == 0) { + if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_curr->v, l_next->v, @@ -310,7 +310,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) l_prev->v, sys->fweights[l_curr_index][1] * sys->vweights[l_curr->v]); } - if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == 0) { + if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_next->v, l_curr->v, @@ -320,7 +320,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) l_prev->v, sys->fweights[l_curr_index][0] * sys->vweights[l_next->v]); } - if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == 0) { + if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == false) { EIG_linear_solver_matrix_add(sys->context, l_prev->v, l_curr->v, @@ -338,7 +338,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys) idv2 = sys->medges[i].v2; /* Is boundary */ if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2] && - sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { + sys->zerola[idv1] == false && sys->zerola[idv2] == false) { EIG_linear_solver_matrix_add( sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); EIG_linear_solver_matrix_add( @@ -358,7 +358,7 @@ static void validate_solution(LaplacianSystem *sys, short flag, float lambda, fl sys->vert_centroid, sys->vertexCos, sys->mpoly, sys->numPolys, sys->mloop); } for (i = 0; i < sys->numVerts; i++) { - if (sys->zerola[i] == 0) { + if (sys->zerola[i] == false) { lam = sys->numNeEd[i] == sys->numNeFa[i] ? (lambda >= 0.0f ? 1.0f : -1.0f) : (lambda_border >= 0.0f ? 1.0f : -1.0f); if (flag & MOD_LAPLACIANSMOOTH_X) { @@ -442,7 +442,7 @@ static void laplaciansmoothModifier_do( wpaint = 1.0f; } - if (sys->zerola[i] == 0) { + if (sys->zerola[i] == false) { if (smd->flag & MOD_LAPLACIANSMOOTH_NORMALIZED) { w = sys->vweights[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda) * wpaint / w; diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index e0507320628..6ef64ad8bc9 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -63,7 +63,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshCacheModifierData), modifier); } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; return (mcmd->play_mode == MOD_MESHCACHE_PLAY_CFEA); diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index c2f9cd8c867..259c1cb2417 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -20,6 +20,7 @@ #include <string.h> +#include "BLI_math_vector.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -39,6 +40,8 @@ #include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -55,18 +58,29 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" -#ifdef WITH_ALEMBIC -# include "ABC_alembic.h" +#if defined(WITH_USD) || defined(WITH_ALEMBIC) # include "BKE_global.h" # include "BKE_lib_id.h" #endif +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +#ifdef WITH_USD +# include "usd.h" +#endif + static void initData(ModifierData *md) { MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mcmd, modifier)); + mcmd->cache_file = NULL; + mcmd->object_path[0] = '\0'; + mcmd->read_flag = MOD_MESHSEQ_READ_ALL; + MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshSeqCacheModifierData), modifier); } @@ -107,9 +121,47 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0'); } +static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh) +{ + BoundBox *bb = BKE_object_boundbox_get(object); + Mesh *result = BKE_mesh_new_nomain_from_template(org_mesh, 8, 0, 0, 24, 6); + + MVert *mvert = result->mvert; + for (int i = 0; i < 8; ++i) { + copy_v3_v3(mvert[i].co, bb->vec[i]); + } + + /* See DNA_object_types.h for the diagram showing the order of the vertices for a BoundBox. */ + static unsigned int loops_v[6][4] = { + {0, 4, 5, 1}, + {4, 7, 6, 5}, + {7, 3, 2, 6}, + {3, 0, 1, 2}, + {1, 5, 6, 2}, + {3, 7, 4, 0}, + }; + + MLoop *mloop = result->mloop; + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 4; ++j, ++mloop) { + mloop->v = loops_v[i][j]; + } + } + + MPoly *mpoly = result->mpoly; + for (int i = 0; i < 6; ++i) { + mpoly[i].loopstart = i * 4; + mpoly[i].totloop = 4; + } + + BKE_mesh_calc_edges(result, false, false); + + return result; +} + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { -#ifdef WITH_ALEMBIC +#if defined(WITH_USD) || defined(WITH_ALEMBIC) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; /* Only used to check whether we are operating on org data or not... */ @@ -127,16 +179,38 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path); if (!mcmd->reader) { BKE_modifier_set_error( - ctx->object, md, "Could not create Alembic reader for file %s", cache_file->filepath); + ctx->object, md, "Could not create reader for file %s", cache_file->filepath); return mesh; } } - /* If this invocation is for the ORCO mesh, and the mesh in Alembic hasn't changed topology, we + /* Do not process data if using a render procedural, return a box instead for displaying in the + * viewport. */ + if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(ctx->depsgraph))) { + return generate_bounding_box_mesh(ctx->object, org_mesh); + } + + /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we * must return the mesh as-is instead of deforming it. */ - if (ctx->flag & MOD_APPLY_ORCO && - !ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { - return mesh; + if (ctx->flag & MOD_APPLY_ORCO) { + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { + return mesh; + } +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + if (!USD_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) { + return mesh; + } +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } } if (me != NULL) { @@ -156,7 +230,23 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } - Mesh *result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + Mesh *result = NULL; + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: +# ifdef WITH_ALEMBIC + result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); +# endif + break; + case CACHEFILE_TYPE_USD: +# ifdef WITH_USD + result = USD_read_mesh( + mcmd->reader, ctx->object, mesh, time * FPS, &err_str, mcmd->read_flag); +# endif + break; + case CACHE_FILE_TYPE_INVALID: + break; + } mcmd->velocity_delta = 1.0f; if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { @@ -180,18 +270,20 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return result ? result : mesh; #else - UNUSED_VARS(ctx, md); + UNUSED_VARS(ctx, md, generate_bounding_box_mesh); return mesh; #endif } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mode) { -#ifdef WITH_ALEMBIC +#if defined(WITH_USD) || defined(WITH_ALEMBIC) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; - return (mcmd->cache_file != NULL); + /* Do not evaluate animations if using the render engine procedural. */ + return (mcmd->cache_file != NULL) && + !BKE_cache_file_uses_render_procedural(mcmd->cache_file, scene, dag_eval_mode); #else - UNUSED_VARS(md); + UNUSED_VARS(scene, md, dag_eval_mode); return false; #endif } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 5fa11ffdd10..620c7ef438a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -84,7 +84,8 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" + +#include "FN_multi_function.hh" using blender::destruct_ptr; using blender::float3; @@ -721,17 +722,17 @@ static void initialize_group_input(NodesModifierData &nmd, return; } if (nmd.settings.properties == nullptr) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, socket.identifier); if (property == nullptr) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } if (!property_type->is_correct_type(*property)) { - blender::nodes::socket_cpp_value_get(socket, r_value); + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } property_type->init_cpp_value(*property, r_value); @@ -858,7 +859,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope); + blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; Map<DOutputSocket, GMutablePointer> group_inputs; @@ -883,7 +884,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, /* Initialize remaining group inputs. */ for (const OutputSocketRef *socket : remaining_input_sockets) { - const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo()); + const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e652eb8353d..5646e37707c 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -294,7 +294,11 @@ class LockedNode : NonCopyable, NonMovable { static const CPPType *get_socket_cpp_type(const SocketRef &socket) { - const CPPType *type = nodes::socket_cpp_type_get(*socket.typeinfo()); + const bNodeSocketType *typeinfo = socket.typeinfo(); + if (typeinfo->get_geometry_nodes_cpp_type == nullptr) { + return nullptr; + } + const CPPType *type = typeinfo->get_geometry_nodes_cpp_type(); if (type == nullptr) { return nullptr; } @@ -310,6 +314,12 @@ static const CPPType *get_socket_cpp_type(const DSocket socket) return get_socket_cpp_type(*socket.socket_ref()); } +static void get_socket_value(const SocketRef &socket, void *r_value) +{ + const bNodeSocketType *typeinfo = socket.typeinfo(); + typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); +} + static bool node_supports_laziness(const DNode node) { return node->typeinfo()->geometry_node_execute_supports_laziness; @@ -826,7 +836,7 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr); + const MultiFunction *multi_function = params_.mf_by_node->try_get(node); if (multi_function != nullptr) { this->execute_multi_function_node(node, *multi_function, node_state); return; @@ -1235,14 +1245,8 @@ class GeometryNodesEvaluator { void *buffer = allocator.allocate(to_type.size(), to_type.alignment()); GMutablePointer value{to_type, buffer}; - if (conversions_.is_convertible(from_type, to_type)) { - /* Do the conversion if possible. */ - conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer); - } - else { - /* Cannot convert, use default value instead. */ - to_type.copy_construct(to_type.default_value(), buffer); - } + this->convert_value(from_type, to_type, value_to_forward.get(), buffer); + /* Multi input socket values are logged once all values are available. */ if (!to_socket->is_multi_input_socket()) { this->log_socket_value({to_socket}, value); @@ -1363,25 +1367,36 @@ class GeometryNodesEvaluator { { LinearAllocator<> &allocator = local_allocators_.local(); - bNodeSocket *bsocket = socket->bsocket(); const CPPType &type = *get_socket_cpp_type(socket); void *buffer = allocator.allocate(type.size(), type.alignment()); - blender::nodes::socket_cpp_value_get(*bsocket, buffer); + get_socket_value(*socket.socket_ref(), buffer); if (type == required_type) { return {type, buffer}; } - if (conversions_.is_convertible(type, required_type)) { - /* Convert the loaded value to the required type if possible. */ - void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); - conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer); - type.destruct(buffer); - return {required_type, converted_buffer}; + void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); + this->convert_value(type, required_type, buffer, converted_buffer); + return {required_type, converted_buffer}; + } + + void convert_value(const CPPType &from_type, + const CPPType &to_type, + const void *from_value, + void *to_value) + { + if (from_type == to_type) { + from_type.copy_construct(from_value, to_value); + return; + } + + if (conversions_.is_convertible(from_type, to_type)) { + /* Do the conversion if possible. */ + conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value); + } + else { + /* Cannot convert, use default value instead. */ + to_type.copy_construct(to_type.default_value(), to_value); } - /* Use a default fallback value when the loaded type is not compatible. */ - void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment()); - required_type.copy_construct(required_type.default_value(), default_buffer); - return {required_type, default_buffer}; } NodeState &get_node_state(const DNode node) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh index f4ee6242dcb..5151be07aa2 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh @@ -10,7 +10,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -20,12 +20,14 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "FN_generic_pointer.hh" #include "DNA_modifier_types.h" +#include "FN_multi_function.hh" + namespace geo_log = blender::nodes::geometry_nodes_eval_log; namespace blender::modifiers::geometry_nodes { @@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams { * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value * and then it can be freed. */ Vector<DSocket> force_compute_sockets; - nodes::MultiFunctionByNode *mf_by_node; + nodes::NodeMultiFunctions *mf_by_node; const NodesModifierData *modifier_; Depsgraph *depsgraph; Object *self_object; diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index e7750f0a0d1..1dbdcf87d63 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -556,15 +556,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, num_polys); CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY); } - BKE_mesh_calc_normals_poly(mvert, - NULL, - num_verts, - mloop, - mpoly, - num_loops, - num_polys, - polynors, - (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) ? false : true); + if (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) { + BKE_mesh_calc_normals_poly_and_vertex( + mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors, NULL); + } + else { + BKE_mesh_calc_normals_poly(mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors); + } result->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 8f3206da5be..1c502b94bdb 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -95,8 +95,9 @@ static void initData(ModifierData *md) BKE_modifier_path_init(omd->cachepath, sizeof(omd->cachepath), "cache_ocean"); omd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution); - simulate_ocean_modifier(omd); + if (BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution)) { + simulate_ocean_modifier(omd); + } #else /* WITH_OCEANSIM */ UNUSED_VARS(md); #endif /* WITH_OCEANSIM */ @@ -132,8 +133,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tomd->oceancache = NULL; tomd->ocean = BKE_ocean_add(); - BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution); - simulate_ocean_modifier(tomd); + if (BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution)) { + simulate_ocean_modifier(tomd); + } #else /* WITH_OCEANSIM */ /* unused */ (void)md; @@ -323,6 +325,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { OceanModifierData *omd = (OceanModifierData *)md; + if (omd->ocean && !BKE_ocean_is_valid(omd->ocean)) { + BKE_modifier_set_error(ctx->object, md, "Failed to allocate memory"); + return mesh; + } int cfra_scene = (int)DEG_get_ctime(ctx->depsgraph); Object *ob = ctx->object; bool allocated_ocean = false; diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d7d2f948955..4187f9087a0 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -62,7 +62,9 @@ static void deformVerts(ModifierData *UNUSED(md), ctx->depsgraph, scene, ctx->object, DEG_get_ctime(ctx->depsgraph), vertexCos, numVerts); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index e97190b1878..00fa6e24a64 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -259,14 +259,12 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* calculate only face normals */ poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); BKE_mesh_calc_normals_poly(orig_mvert, - NULL, (int)numVerts, orig_mloop, - orig_mpoly, (int)numLoops, + orig_mpoly, (int)numPolys, - poly_nors, - true); + poly_nors); } STACK_INIT(new_vert_arr, numVerts * 2); @@ -507,8 +505,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */ if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { /* no even thickness, very simple */ - float scalar_short; - float scalar_short_vgroup; + float ofs_new_vgroup; /* for clamping */ float *vert_lens = NULL; @@ -597,7 +594,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex uint i_orig, i_end; bool do_shell_align; - scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; + ofs_new_vgroup = ofs_new; INIT_VERT_ARRAY_OFFSETS(false); @@ -606,36 +603,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) { - scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); } else { - scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index); } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; + ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new; } if (do_clamp && offset > FLT_EPSILON) { /* always reset because we may have set before */ if (dvert == NULL) { - scalar_short_vgroup = scalar_short; + ofs_new_vgroup = ofs_new; } if (do_angle_clamp) { float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f); if (cos_ang > 0) { float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; if (max_off < offset * 0.5f) { - scalar_short_vgroup *= max_off / offset * 2; + ofs_new_vgroup *= max_off / offset * 2; } } } else { if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; + ofs_new_vgroup *= scalar; } } } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + if (vert_nors) { + madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup); + } + else { + madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f); + } } } @@ -643,7 +644,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex uint i_orig, i_end; bool do_shell_align; - scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; + ofs_new_vgroup = ofs_orig; /* as above but swapped */ INIT_VERT_ARRAY_OFFSETS(true); @@ -653,36 +654,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) { - scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); } else { - scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index); + ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index); } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; + ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig; } if (do_clamp && offset > FLT_EPSILON) { /* always reset because we may have set before */ if (dvert == NULL) { - scalar_short_vgroup = scalar_short; + ofs_new_vgroup = ofs_orig; } if (do_angle_clamp) { float cos_ang = cosf(vert_angs[i_orig] * 0.5f); if (cos_ang > 0) { float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; if (max_off < offset * 0.5f) { - scalar_short_vgroup *= max_off / offset * 2; + ofs_new_vgroup *= max_off / offset * 2; } } } else { if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; + ofs_new_vgroup *= scalar; } } } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + if (vert_nors) { + madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup); + } + else { + madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f); + } } } diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index b872f04b60f..5b4716a1a43 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -71,6 +71,15 @@ static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3], return angle; } +static float clamp_nonzero(const float value, const float epsilon) +{ + BLI_assert(!(epsilon < 0.0f)); + if (value < 0.0f) { + return min_ff(value, -epsilon); + } + return max_ff(value, epsilon); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -164,8 +173,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; const float ofs_back = ofs_front - smd->offset * smd->offset_fac; - const float ofs_front_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_front : ofs_back)); - const float ofs_back_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_back : ofs_front)); + const float ofs_front_clamped = clamp_nonzero(smd->offset > 0 ? ofs_front : ofs_back, 1e-5f); + const float ofs_back_clamped = clamp_nonzero(smd->offset > 0 ? ofs_back : ofs_front, 1e-5f); const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const float offset = fabsf(smd->offset) * smd->offset_clamp; @@ -202,15 +211,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, /* Calculate only face normals. */ poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); - BKE_mesh_calc_normals_poly(orig_mvert, - NULL, - (int)numVerts, - orig_mloop, - orig_mpoly, - (int)numLoops, - (int)numPolys, - poly_nors, - true); + BKE_mesh_calc_normals_poly( + orig_mvert, (int)numVerts, orig_mloop, (int)numLoops, orig_mpoly, (int)numPolys, poly_nors); NewFaceRef *face_sides_arr = MEM_malloc_arrayN( numPolys * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify"); diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index ce427281db3..db0b769684e 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -358,13 +358,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel) /* Don't show adaptive options if the cycles experimental feature set is disabled. */ Scene *scene = CTX_data_scene(C); - PointerRNA scene_ptr; - RNA_id_pointer_create(&scene->id, &scene_ptr); - if (BKE_scene_uses_cycles(scene)) { - PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); - if (RNA_enum_get(&cycles_ptr, "feature_set") != 1) { /* EXPERIMENTAL */ - return false; - } + if (!BKE_scene_uses_cycles_experimental_features(scene)) { + return false; } return true; diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index bfd4cd81803..3f2d0a06db8 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -98,7 +98,9 @@ static void freeData(ModifierData *md) } } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index af4b31d6bfc..fcf75040a9a 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -95,7 +95,9 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void walk(userData, ob, md, "texture"); } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md); if (vdmd->texture) { diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 3bebc52c503..25e33b22bde 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -116,7 +116,9 @@ static void matrix_from_obj_pchan(float mat[4][4], } } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WarpModifierData *wmd = (WarpModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index cf4c195c66d..03f8e3a1dfb 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -70,7 +70,9 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier); } -static bool dependsOnTime(ModifierData *UNUSED(md)) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *UNUSED(md), + const int UNUSED(dag_eval_mode)) { return true; } diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index 3b147c69716..1ee64b935b7 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -615,8 +615,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys); CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY); } - BKE_mesh_calc_normals_poly( - mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false); + BKE_mesh_calc_normals_poly_and_vertex( + mvert, numVerts, mloop, numLoops, mpoly, numPolys, polynors, NULL); const float split_angle = mesh->smoothresh; short(*clnors)[2]; diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 093fa118ee0..a9d01c64ff1 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -112,7 +112,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 7aae089fa18..b369b82ebb7 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -154,7 +154,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 6e78774269a..7ee19e1c537 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -362,7 +362,9 @@ static void requiredDataMask(Object *UNUSED(ob), /* No need to ask for CD_PREVIEW_MLOOPCOL... */ } -static bool dependsOnTime(ModifierData *md) +static bool dependsOnTime(struct Scene *UNUSED(scene), + ModifierData *md, + const int UNUSED(dag_eval_mode)) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 36e5be6a292..8680fcee49a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -175,7 +175,9 @@ set(SRC geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc + geometry/nodes/node_geo_curve_select_by_handle_type.cc geometry/nodes/node_geo_curve_set_handles.cc + geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc @@ -343,11 +345,10 @@ set(SRC intern/node_common.c intern/node_exec.cc intern/node_geometry_exec.cc + intern/node_multi_function.cc intern/node_socket.cc - intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/node_util.c - intern/type_callbacks.cc intern/type_conversions.cc composite/node_composite_util.h @@ -364,13 +365,12 @@ set(SRC NOD_geometry_exec.hh NOD_geometry_nodes_eval_log.hh NOD_math_functions.hh - NOD_node_tree_multi_function.hh + NOD_multi_function.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h NOD_static_types.h NOD_texture.h - NOD_type_callbacks.hh NOD_type_conversions.hh intern/node_common.h intern/node_exec.h diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 868fcbb33af..856d787c8d0 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -63,6 +63,7 @@ void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_set_handles(void); +void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); @@ -94,6 +95,7 @@ void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_subdivision_surface(void); diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh new file mode 100644 index 00000000000..2f4b104fb4c --- /dev/null +++ b/source/blender/nodes/NOD_multi_function.hh @@ -0,0 +1,130 @@ +/* + * 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 "FN_multi_function.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::nodes { + +using namespace fn::multi_function_types; + +class NodeMultiFunctions; + +/** + * Utility class to help nodes build a multi-function for themselves. + */ +class NodeMultiFunctionBuilder : NonCopyable, NonMovable { + private: + ResourceScope &resource_scope_; + bNode &node_; + bNodeTree &tree_; + const MultiFunction *built_fn_ = nullptr; + + friend NodeMultiFunctions; + + public: + NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + + /** + * Assign a multi-function for the current node. The input and output parameters of the function + * have to match the available sockets in the node. + */ + void set_matching_fn(const MultiFunction *fn); + void set_matching_fn(const MultiFunction &fn); + + /** + * Utility method for creating and assigning a multi-function when it can't have a static + * lifetime. + */ + template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args); + + bNode &node(); + bNodeTree &tree(); + + ResourceScope &resource_scope(); +}; + +/** + * Gives access to multi-functions for all nodes in a node tree that support them. + */ +class NodeMultiFunctions { + private: + Map<const bNode *, const MultiFunction *> map_; + + public: + NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + + const MultiFunction *try_get(const DNode &node) const; +}; + +/* -------------------------------------------------------------------- + * NodeMultiFunctionBuilder inline methods. + */ + +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, + bNode &node, + bNodeTree &tree) + : resource_scope_(resource_scope), node_(node), tree_(tree) +{ +} + +inline bNode &NodeMultiFunctionBuilder::node() +{ + return node_; +} + +inline bNodeTree &NodeMultiFunctionBuilder::tree() +{ + return tree_; +} + +inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() +{ + return resource_scope_; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) +{ + built_fn_ = fn; +} + +inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) +{ + this->set_matching_fn(&fn); +} + +template<typename T, typename... Args> +inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) +{ + const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...); + this->set_matching_fn(&fn); +} + +/* -------------------------------------------------------------------- + * NodeMultiFunctions inline methods. + */ + +inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +{ + return map_.lookup_default(node->bnode(), nullptr); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh deleted file mode 100644 index 7eeeaef0b98..00000000000 --- a/source/blender/nodes/NOD_node_tree_multi_function.hh +++ /dev/null @@ -1,390 +0,0 @@ -/* - * 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 nodes - * - * This file allows you to generate a multi-function network from a user-generated node tree. - */ - -#include "FN_multi_function_builder.hh" -#include "FN_multi_function_network.hh" - -#include "NOD_derived_node_tree.hh" -#include "NOD_type_callbacks.hh" - -#include "BLI_multi_value_map.hh" -#include "BLI_resource_scope.hh" - -namespace blender::nodes { - -/** - * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This - * is necessary for further processing of a multi-function network that has been generated from a - * node tree. - */ -class MFNetworkTreeMap { - private: - /** - * Store by id instead of using a hash table to avoid unnecessary hash table lookups. - * - * Input sockets in a node tree can have multiple corresponding sockets in the generated - * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes. - */ - const DerivedNodeTree &tree_; - fn::MFNetwork &network_; - MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_; - - public: - MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) - : tree_(tree), network_(network) - { - } - - const DerivedNodeTree &tree() const - { - return tree_; - } - - const fn::MFNetwork &network() const - { - return network_; - } - - fn::MFNetwork &network() - { - return network_; - } - - void add(const DSocket &dsocket, fn::MFSocket &socket) - { - BLI_assert(dsocket->is_input() == socket.is_input()); - BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) - { - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) - { - /* There can be at most one matching output socket. */ - BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty()); - sockets_by_dsocket_.add(dsocket, &socket); - } - - void add(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DInputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - assert_same_size(dsockets, sockets); - for (int i : dsockets.index_range()) { - this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]); - } - } - - void add_try_match(const DNode &dnode, fn::MFNode &node) - { - this->add_try_match(*dnode.context(), - dnode->inputs().cast<const SocketRef *>(), - node.inputs().cast<fn::MFSocket *>()); - this->add_try_match(*dnode.context(), - dnode->outputs().cast<const SocketRef *>(), - node.outputs().cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const InputSocketRef *> dsockets, - Span<fn::MFInputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const OutputSocketRef *> dsockets, - Span<fn::MFOutputSocket *> sockets) - { - this->add_try_match( - context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>()); - } - - void add_try_match(const DTreeContext &context, - Span<const SocketRef *> dsockets, - Span<fn::MFSocket *> sockets) - { - int used_sockets = 0; - for (const SocketRef *dsocket : dsockets) { - if (!dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*dsocket->typeinfo())) { - continue; - } - fn::MFSocket *socket = sockets[used_sockets]; - this->add(DSocket(&context, dsocket), *socket); - used_sockets++; - } - } - - fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket)[0]->as_output(); - } - - Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket) - { - return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>(); - } - - fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) - { - Span<fn::MFInputSocket *> sockets = this->lookup(dsocket); - BLI_assert(sockets.size() == 1); - fn::MFInputSocket &socket = *sockets[0]; - BLI_assert(socket.node().is_dummy()); - return socket; - } - - fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket) - { - fn::MFOutputSocket &socket = this->lookup(dsocket); - BLI_assert(socket.node().is_dummy()); - return socket; - } - - bool is_mapped(const DSocket &dsocket) const - { - return !sockets_by_dsocket_.lookup(dsocket).is_empty(); - } -}; - -/** - * This data is necessary throughout the generation of a MFNetwork from a node tree. - */ -struct CommonMFNetworkBuilderData { - ResourceScope &scope; - fn::MFNetwork &network; - MFNetworkTreeMap &network_map; - const DerivedNodeTree &tree; -}; - -class MFNetworkBuilderBase { - protected: - CommonMFNetworkBuilderData &common_; - - public: - MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common) - { - } - - /** - * Returns the network that is currently being built. - */ - fn::MFNetwork &network() - { - return common_.network; - } - - /** - * Returns the map between the node tree and the multi-function network that is being built. - */ - MFNetworkTreeMap &network_map() - { - return common_.network_map; - } - - /** - * Returns a resource collector that will only be destructed after the multi-function network is - * destructed. - */ - ResourceScope &resource_scope() - { - return common_.scope; - } - - /** - * Constructs a new function that will live at least as long as the MFNetwork. - */ - template<typename T, typename... Args> T &construct_fn(Args &&...args) - { - BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), ""); - void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); - T *fn = new (buffer) T(std::forward<Args>(args)...); - common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str()); - return *fn; - } -}; - -/** - * This class is used by socket implementations to define how an unlinked input socket is handled - * in a multi-function network. - */ -class SocketMFNetworkBuilder : public MFNetworkBuilderBase { - private: - bNodeSocket *bsocket_; - fn::MFOutputSocket *built_socket_ = nullptr; - - public: - SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) - : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket()) - { - } - - /** - * Returns the socket that is currently being built. - */ - bNodeSocket &bsocket() - { - return *bsocket_; - } - - /** - * Utility method that returns bsocket->default_value for the current socket. - */ - template<typename T> T *socket_default_value() - { - return static_cast<T *>(bsocket_->default_value); - } - - /** - * Builds a function node for that socket that outputs the given constant value. - */ - template<typename T> void set_constant_value(T value) - { - this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value)); - } - void set_constant_value(const CPPType &type, const void *value) - { - /* The value has live as long as the generated mf network. */ - this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value); - } - - template<typename T, typename... Args> void construct_generator_fn(Args &&...args) - { - const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_generator_fn(fn); - } - - /** - * Uses the first output of the given multi-function as value of the socket. - */ - void set_generator_fn(const fn::MultiFunction &fn) - { - fn::MFFunctionNode &node = common_.network.add_function(fn); - this->set_socket(node.output(0)); - } - - /** - * Define a multi-function socket that outputs the value of the bsocket. - */ - void set_socket(fn::MFOutputSocket &socket) - { - built_socket_ = &socket; - } - - fn::MFOutputSocket *built_socket() - { - return built_socket_; - } -}; - -/** - * This class is used by node implementations to define how a user-level node expands into - * multi-function nodes internally. - */ -class NodeMFNetworkBuilder : public MFNetworkBuilderBase { - private: - DNode dnode_; - - public: - NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode) - : MFNetworkBuilderBase(common), dnode_(dnode) - { - } - - /** - * Tells the builder to build a function that corresponds to the node that is being built. It - * will try to match up sockets. - */ - template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args) - { - T &function = this->construct_fn<T>(std::forward<Args>(args)...); - this->set_matching_fn(function); - return function; - } - - const fn::MultiFunction &get_not_implemented_fn() - { - return this->get_default_fn("Not Implemented (" + dnode_->name() + ")"); - } - - const fn::MultiFunction &get_default_fn(StringRef name); - - const void set_not_implemented() - { - this->set_matching_fn(this->get_not_implemented_fn()); - } - - /** - * Tells the builder that the given function corresponds to the node that is being built. It will - * try to match up sockets. For that it skips unavailable and non-data sockets. - */ - void set_matching_fn(const fn::MultiFunction &function) - { - fn::MFFunctionNode &node = common_.network.add_function(function); - common_.network_map.add_try_match(dnode_, node); - } - - /** - * Returns the node that is currently being built. - */ - bNode &bnode() - { - return *dnode_->bnode(); - } - - /** - * Returns the node that is currently being built. - */ - const DNode &dnode() const - { - return dnode_; - } -}; - -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope); - -using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>; -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); - -} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a091f28f3a0..4da8648173d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -61,7 +61,7 @@ DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRG DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" ) -DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular", "" ) +DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" ) @@ -303,6 +303,8 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by 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_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") @@ -335,7 +337,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") -DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") +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_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c index 81e2408fcf9..a5906c31093 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c @@ -1,6 +1,4 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * * 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 @@ -19,10 +17,6 @@ * All rights reserved. * * The Original Code is: all of this file. - * - * Contributor(s): IRIE Shinsuke - * - * ***** END GPL LICENSE BLOCK ***** */ /** \file diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c index 3ffad8216de..ec5c79cc087 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.c +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c @@ -132,7 +132,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); - if ((srv->viewflag & SCE_VIEW_DISABLE)) { + if (srv->viewflag & SCE_VIEW_DISABLE) { sock->flag |= SOCK_HIDDEN; } } diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 9fbd6712827..96a8f29c3e9 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -30,7 +30,7 @@ #include "BLT_translation.h" #include "NOD_function.h" -#include "NOD_node_tree_multi_function.hh" +#include "NOD_multi_function.hh" #include "node_util.h" 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 7a83ff8e016..0ba9080918c 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ "And", [](bool a, bool b) { return a && b; }}; @@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: - return and_fn; + return &and_fn; case NODE_BOOLEAN_MATH_OR: - return or_fn; + return &or_fn; case NODE_BOOLEAN_MATH_NOT: - return not_fn; + return ¬_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_boolean_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -93,7 +94,7 @@ void register_node_type_fn_boolean_math() node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); - ntype.expand_in_mf_network = node_boolean_expand_in_mf_network; + ntype.build_multi_function = fn_node_boolean_math_build_multi_function; ntype.draw_buttons = fn_node_boolean_math_layout; nodeRegisterType(&ntype); } 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 6c8df8f2ea0..16ffb761a15 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &node) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; @@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node) switch (node.custom1) { case NODE_FLOAT_COMPARE_LESS_THAN: - return less_than_fn; + return &less_than_fn; case NODE_FLOAT_COMPARE_LESS_EQUAL: - return less_equal_fn; + return &less_equal_fn; case NODE_FLOAT_COMPARE_GREATER_THAN: - return greater_than_fn; + return &greater_than_fn; case NODE_FLOAT_COMPARE_GREATER_EQUAL: - return greater_equal_fn; + return &greater_equal_fn; case NODE_FLOAT_COMPARE_EQUAL: - return equal_fn; + return &equal_fn; case NODE_FLOAT_COMPARE_NOT_EQUAL: - return not_equal_fn; + return ¬_equal_fn; } - BLI_assert(false); - return blender::fn::dummy_multi_function; + BLI_assert_unreachable(); + return nullptr; } -static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_compare_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -112,7 +113,7 @@ void register_node_type_fn_float_compare() node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); - ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_compare_build_multi_function; ntype.draw_buttons = geo_node_float_compare_layout; nodeRegisterType(&ntype); } 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 26cde576400..52acfefe615 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 @@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) { static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round", [](float a) { return (int)round(a); }}; @@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: - return round_fn; + return &round_fn; case FN_NODE_FLOAT_TO_INT_FLOOR: - return floor_fn; + return &floor_fn; case FN_NODE_FLOAT_TO_INT_CEIL: - return ceil_fn; + return &ceil_fn; case FN_NODE_FLOAT_TO_INT_TRUNCATE: - return trunc_fn; + return &trunc_fn; } BLI_assert_unreachable(); - return blender::fn::dummy_multi_function; + return nullptr; } -static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_float_to_int_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -89,7 +90,7 @@ void register_node_type_fn_float_to_int() fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); node_type_label(&ntype, node_float_to_int_label); - ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network; + ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; nodeRegisterType(&ntype); } 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 f16bdef2f38..560ace57aba 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_input_string_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(string); + builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>( + std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -78,7 +78,7 @@ void register_node_type_fn_input_string() node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network; + ntype.build_multi_function = fn_node_input_string_build_multi_function; ntype.draw_buttons = fn_node_input_string_layout; nodeRegisterType(&ntype); } 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 2cd4eb1d9df..244c045de9a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_vector_input_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage); blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector); } - static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -58,7 +56,7 @@ void register_node_type_fn_input_vector() node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network; + ntype.build_multi_function = fn_node_vector_input_build_multi_function; ntype.draw_buttons = fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index a3c9f44b6a1..47ec9adf6bd 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void fn_node_random_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - builder.construct_and_set_matching_fn<RandomFloatFunction>(); + static RandomFloatFunction fn; + builder.set_matching_fn(fn); } void register_node_type_fn_random_float() @@ -79,6 +80,6 @@ void register_node_type_fn_random_float() fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); - ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network; + ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index e0a3f5ad334..5f02061da97 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -30,7 +30,7 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_TEXTURE, N_("Texture")}, + {SOCK_TEXTURE, N_("Texture"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL}, {SOCK_STRING, N_("Mapping")}, {SOCK_STRING, N_("Result")}, {-1, ""}, 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 4286db52115..91e08d7777b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -150,7 +150,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) plConvexHullDelete(hull); - BKE_mesh_calc_normals(result); + BKE_mesh_normals_tag_dirty(result); return result; } @@ -304,6 +304,8 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params) } params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh)); #else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without Bullet")); params.set_output("Convex Hull", geometry_set); #endif /* WITH_BULLET */ } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc new file mode 100644 index 00000000000..fb21c05b0c0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc @@ -0,0 +1,147 @@ +/* + * 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 "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_select_by_handle_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSelectHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +namespace blender::nodes { + +static void select_curve_by_handle_type(const CurveEval &curve, + const BezierSpline::HandleType type, + const GeometryNodeCurveHandleMode mode, + const MutableSpan<bool> r_selection) +{ + const Array<int> offsets = curve.control_point_offsets(); + Span<SplinePtr> splines = curve.splines(); + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i_spline : range) { + const Spline &spline = *splines[i_spline]; + if (spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); + Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left(); + Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right(); + for (const int i_point : IndexRange(bezier_spline.size())) { + r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && + types_left[i_point] == type) || + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && + types_right[i_point] == type); + } + } + else { + r_selection.slice(offsets[i_spline], offsets[i_spline + 1]).fill(false); + } + } + }); +} + +static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSelectHandles *storage = + (const NodeGeometryCurveSelectHandles *)params.node().storage; + const BezierSpline::HandleType handle_type = handle_type_from_input_type( + (GeometryNodeCurveHandleType)storage->handle_type); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + const CurveEval *curve = curve_component.get_for_read(); + + if (curve != nullptr) { + const std::string selection_name = params.extract_input<std::string>("Selection"); + OutputAttribute_Typed<bool> selection = + curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT); + if (selection) { + select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span()); + selection.save(); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_select_by_handle_type() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_select_by_handle_type_in, geo_node_select_by_handle_type_out); + ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; + node_type_init(&ntype, geo_node_curve_select_by_handle_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSelectHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = geo_node_curve_select_by_handle_type_layout; + + nodeRegisterType(&ntype); +} 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 new file mode 100644 index 00000000000..fe3f42625ae --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -0,0 +1,307 @@ +/* + * 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 "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_spline_type_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_spline_type_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_spline_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( + sizeof(NodeGeometryCurveSplineType), __func__); + + data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; + node->storage = data; +} + +template<class T> +static void scale_input_assign(const Span<T> input, + const int scale, + const int offset, + const MutableSpan<T> r_output) +{ + for (const int i : IndexRange(r_output.size())) { + r_output[i] = input[i * scale + offset]; + } +} + +template<class T> +static void scale_output_assign(const Span<T> input, + const int scale, + const int offset, + const MutableSpan<T> &r_output) +{ + for (const int i : IndexRange(input.size())) { + r_output[i * scale + offset] = input[i]; + } +} + +template<typename CopyFn> +static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) +{ + input_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = input_spline.attributes.get_for_read(name); + BLI_assert(src); + if (!output_spline.attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(name); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + + copy_fn(*src, *dst); + + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr convert_to_poly_spline(const Spline &input) +{ + std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr poly_to_nurbs(const Spline &input) +{ + std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + Spline::copy_base_settings(input, *output); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes = input.attributes; + return output; +} + +static SplinePtr bezier_to_nurbs(const Spline &input) +{ + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input); + std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); + output->resize(input.size() * 3); + + scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); + scale_output_assign(input.radii(), 3, 0, output->radii()); + scale_output_assign(input.tilts(), 3, 0, output->tilts()); + + scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); + scale_output_assign(input.radii(), 3, 1, output->radii()); + scale_output_assign(input.tilts(), 3, 1, output->tilts()); + + scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); + scale_output_assign(input.radii(), 3, 2, output->radii()); + scale_output_assign(input.tilts(), 3, 2, output->tilts()); + + Spline::copy_base_settings(input, *output); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + output->set_cyclic(input.is_cyclic()); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>()); + scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>()); + scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>()); + }); + }); + return output; +} + +static SplinePtr poly_to_bezier(const Spline &input) +{ + std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); + output->resize(input.size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Vector); + output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->set_resolution(12); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr nurbs_to_bezier(const Spline &input) +{ + const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input); + std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); + output->resize(input.size() / 3); + scale_input_assign<float3>(input.positions(), 3, 1, output->positions()); + scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left()); + scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right()); + scale_input_assign<float>(input.radii(), 3, 2, output->radii()); + scale_input_assign<float>(input.tilts(), 3, 2, output->tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Align); + output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->set_resolution(nurbs_spline.resolution()); + Spline::copy_base_settings(input, *output); + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>()); + }); + }); + return output; +} + +static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) +{ + switch (input.type()) { + case Spline::Type::Bezier: + return input.copy(); + case Spline::Type::Poly: + return poly_to_bezier(input); + case Spline::Type::NURBS: + if (input.size() < 6) { + params.error_message_add( + NodeWarningType::Info, + TIP_("NURBS must have minimum of 6 points for Bezier Conversion")); + return input.copy(); + } + else { + if (input.size() % 3 != 0) { + params.error_message_add(NodeWarningType::Info, + TIP_("NURBS must have multiples of 3 points for full Bezier " + "conversion, curve truncated")); + } + return nurbs_to_bezier(input); + } + } + BLI_assert_unreachable(); + return {}; +} + +static SplinePtr convert_to_nurbs(const Spline &input) +{ + switch (input.type()) { + case Spline::Type::NURBS: + return input.copy(); + case Spline::Type::Bezier: + return bezier_to_nurbs(input); + case Spline::Type::Poly: + return poly_to_nurbs(input); + } + BLI_assert_unreachable(); + return {}; +} + +static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSplineType *storage = + (const NodeGeometryCurveSplineType *)params.node().storage; + const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); + const CurveEval &curve = *curve_component->get_for_read(); + + const std::string selection_name = params.extract_input<std::string>("Selection"); + GVArray_Typed<bool> selection = curve_component->attribute_get_for_read( + selection_name, ATTR_DOMAIN_CURVE, true); + + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + for (const int i : curve.splines().index_range()) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + break; + } + } + else { + new_curve->add_spline(curve.splines()[i]->copy()); + } + } + + new_curve->attributes = curve.attributes; + params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_spline_type() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_curve_spline_type_in, geo_node_curve_spline_type_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSplineType", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = geo_node_curve_spline_type_layout; + + nodeRegisterType(&ntype); +} 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 667e1c931bd..131f9548b40 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 @@ -126,11 +126,11 @@ static Mesh *create_circle_mesh(const float radius, MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); - for (MVert &vert : verts) { - copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); - angle += angle_delta; + /* Assign vertex coordinates. */ + const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num)); + for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; + copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); } if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { copy_v3_v3(verts.last().co, float3(0)); @@ -211,6 +211,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) const float radius = params.extract_input<float>("Radius"); 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()); return; } 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 925ed0f8da8..b834f5e2fa0 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 @@ -299,7 +299,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, mesh->medge[0].v1 = 0; mesh->medge[0].v2 = 1; mesh->medge[0].flag |= ME_LOOSEEDGE; - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); return mesh; } @@ -318,9 +318,9 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, /* Calculate vertex positions. */ const int top_verts_start = 0; const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); + const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num)); for (const int i : IndexRange(verts_num)) { + const float angle = i * angle_delta; const float x = std::cos(angle); const float y = std::sin(angle); if (!top_is_point) { @@ -330,7 +330,6 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, copy_v3_v3(verts[bottom_verts_start + i].co, float3(x * radius_bottom, y * radius_bottom, -height)); } - angle += angle_delta; } if (top_is_point) { copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); @@ -534,12 +533,10 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, } } - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type); - BLI_assert(BKE_mesh_is_valid(mesh)); - return mesh; } @@ -553,6 +550,7 @@ static void geo_node_mesh_primitive_cone_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()); return; } 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 1767f765da4..b40cb478b03 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 @@ -70,6 +70,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const float depth = params.extract_input<float>("Depth"); 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()); return; } 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 ac2f5a23a4d..410290c79ee 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 @@ -79,19 +79,17 @@ static Mesh *create_grid_mesh(const int verts_x, MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; { - const float dx = size_x / edges_x; - const float dy = size_y / edges_y; - float x = -size_x * 0.5; + const float dx = edges_x == 0 ? 0.0f : size_x / edges_x; + const float dy = edges_y == 0 ? 0.0f : size_y / edges_y; + const float x_shift = edges_x / 2.0f; + const float y_shift = edges_y / 2.0f; for (const int x_index : IndexRange(verts_x)) { - float y = -size_y * 0.5; for (const int y_index : IndexRange(verts_y)) { const int vert_index = x_index * verts_y + y_index; - verts[vert_index].co[0] = x; - verts[vert_index].co[1] = y; + verts[vert_index].co[0] = (x_index - x_shift) * dx; + verts[vert_index].co[1] = (y_index - y_shift) * dy; verts[vert_index].co[2] = 0.0f; - y += dy; } - x += dx; } } @@ -162,6 +160,12 @@ 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 < 2 || verts_y < 2) { + if (verts_x < 2) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices X must be at least 2")); + } + if (verts_y < 2) { + params.error_message_add(NodeWarningType::Info, TIP_("Vertices Y must be at least 2")); + } params.set_output("Geometry", GeometrySet()); return; } 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 a193c05daa1..2e6d8ca34c8 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 @@ -118,11 +118,9 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int short normal[3]; normal_float_to_short_v3(normal, delta.normalized()); - float3 co = start; for (const int i : verts.index_range()) { - copy_v3_v3(verts[i].co, co); + copy_v3_v3(verts[i].co, start + delta * i); copy_v3_v3_short(verts[i].no, normal); - co += delta; } fill_edge_data(edges); 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 599c59e4a2e..affba602234 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 @@ -69,26 +69,24 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, const int rings) { const float delta_theta = M_PI / rings; - const float delta_phi = (2 * M_PI) / segments; + const float delta_phi = (2.0f * M_PI) / segments; copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f)); int vert_index = 1; - float theta = delta_theta; - for (const int UNUSED(ring) : IndexRange(rings - 1)) { - float phi = 0.0f; - const float z = cosf(theta); - for (const int UNUSED(segment) : IndexRange(segments)) { + for (const int ring : IndexRange(1, rings - 1)) { + const float theta = ring * delta_theta; + const float z = std::cos(theta); + for (const int segment : IndexRange(1, segments)) { + const float phi = segment * delta_phi; const float sin_theta = std::sin(theta); const float x = sin_theta * std::cos(phi); const float y = sin_theta * std::sin(phi); copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z)); - phi += delta_phi; vert_index++; } - theta += delta_theta; } copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius)); @@ -291,6 +289,12 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) const int segments_num = params.extract_input<int>("Segments"); const int rings_num = params.extract_input<int>("Rings"); if (segments_num < 3 || rings_num < 2) { + if (segments_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Segments must be at least 3")); + } + if (rings_num < 3) { + params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); + } params.set_output("Geometry", GeometrySet()); return; } 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 245d7800621..165da8ec9f2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -88,7 +88,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_calc_normals(mesh_out); + BKE_mesh_normals_tag_dirty(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); mesh_component.replace(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 0c1d8645411..88e3bf17d43 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -63,20 +63,20 @@ static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } +namespace blender::nodes { + static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; - blender::nodes::update_attribute_input_socket_availabilities( + update_attribute_input_socket_availabilities( *node, "Ray Direction", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); - blender::nodes::update_attribute_input_socket_availabilities( + update_attribute_input_socket_availabilities( *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); } -namespace blender::nodes { - -static void raycast_to_mesh(const Mesh *mesh, +static void raycast_to_mesh(const Mesh &mesh, const VArray<float3> &ray_origins, const VArray<float3> &ray_directions, const VArray<float> &ray_lengths, @@ -95,62 +95,64 @@ static void raycast_to_mesh(const Mesh *mesh, BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 4); - - if (tree_data.tree != nullptr) { - for (const int i : ray_origins.index_range()) { - const float ray_length = ray_lengths[i]; - const float3 ray_origin = ray_origins[i]; - const float3 ray_direction = ray_directions[i].normalized(); - - BVHTreeRayHit hit; - hit.index = -1; - hit.dist = ray_length; - if (BLI_bvhtree_ray_cast(tree_data.tree, - ray_origin, - ray_direction, - 0.0f, - &hit, - tree_data.raycast_callback, - &tree_data) != -1) { - if (!r_hit.is_empty()) { - r_hit[i] = hit.index >= 0; - } - if (!r_hit_indices.is_empty()) { - /* Index should always be a valid looptri index, use 0 when hit failed. */ - r_hit_indices[i] = max_ii(hit.index, 0); - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = hit.co; - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = hit.no; - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = hit.dist; - } + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + if (tree_data.tree == nullptr) { + free_bvhtree_from_mesh(&tree_data); + return; + } + + for (const int i : ray_origins.index_range()) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = ray_directions[i].normalized(); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* Index should always be a valid looptri index, use 0 when hit failed. */ + r_hit_indices[i] = max_ii(hit.index, 0); + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; } - else { - if (!r_hit.is_empty()) { - r_hit[i] = false; - } - if (!r_hit_indices.is_empty()) { - r_hit_indices[i] = 0; - } - if (!r_hit_positions.is_empty()) { - r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_normals.is_empty()) { - r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); - } - if (!r_hit_distances.is_empty()) { - r_hit_distances[i] = ray_length; - } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = 0; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; } } - - free_bvhtree_from_mesh(&tree_data); } + + free_bvhtree_from_mesh(&tree_data); } static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( @@ -166,7 +168,7 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( } static void raycast_from_points(const GeoNodeExecParams ¶ms, - const GeometrySet &src_geometry, + const GeometrySet &target_geometry, GeometryComponent &dst_component, const StringRef hit_name, const StringRef hit_position_name, @@ -177,7 +179,8 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, { BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); - const MeshComponent *src_mesh_component = src_geometry.get_component_for_read<MeshComponent>(); + const MeshComponent *src_mesh_component = + target_geometry.get_component_for_read<MeshComponent>(); if (src_mesh_component == nullptr) { return; } @@ -211,8 +214,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain); /* Positions and looptri indices are always needed for interpolation, - * so create temporary arrays if no output attribute is given. - */ + * so create temporary arrays if no output attribute is given. */ Array<int> hit_indices; Array<float3> hit_positions_internal; if (!hit_attribute_names.is_empty()) { @@ -232,7 +234,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, hit_distance_attribute.as_span() : MutableSpan<float>(); - raycast_to_mesh(src_mesh, + raycast_to_mesh(*src_mesh, ray_origins, ray_directions, ray_lengths, @@ -268,34 +270,32 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, static void geo_node_raycast_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - GeometrySet cast_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); + GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); const std::string hit_name = params.extract_input<std::string>("Is Hit"); const std::string hit_position_name = params.extract_input<std::string>("Hit Position"); const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal"); const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance"); - const Array<std::string> hit_attribute_names = { - params.extract_input<std::string>("Target Attribute")}; - const Array<std::string> hit_attribute_output_names = { - params.extract_input<std::string>("Hit Attribute")}; + const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")}; + const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")}; geometry_set = bke::geometry_set_realize_instances(geometry_set); - cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set); + target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); - static const Array<GeometryComponentType> SupportedTypes = { + static const Array<GeometryComponentType> types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (GeometryComponentType geo_type : SupportedTypes) { - if (geometry_set.has(geo_type)) { + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { raycast_from_points(params, - cast_geometry_set, - geometry_set.get_component_for_write(geo_type), + target_geometry_set, + geometry_set.get_component_for_write(type), hit_name, hit_position_name, hit_normal_name, hit_distance_name, - hit_attribute_names, - hit_attribute_output_names); + hit_names, + hit_output_names); } } @@ -312,7 +312,7 @@ void register_node_type_geo_raycast() node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, geo_node_raycast_init); - node_type_update(&ntype, geo_node_raycast_update); + node_type_update(&ntype, blender::nodes::geo_node_raycast_update); node_type_storage( &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; 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 f0f032ed8f4..4f70252ae75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -18,17 +18,15 @@ #include "BKE_subdiv.h" #include "BKE_subdiv_mesh.h" +#include "DNA_modifier_types.h" #include "UI_interface.h" #include "UI_resources.h" - #include "node_geometry_util.hh" static bNodeSocketTemplate geo_node_subdivision_surface_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6}, {SOCK_BOOLEAN, N_("Use Creases")}, - {SOCK_BOOLEAN, N_("Boundary Smooth"), true}, - {SOCK_BOOLEAN, N_("Smooth UVs")}, {-1, ""}, }; @@ -37,6 +35,30 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = { {-1, ""}, }; +static void geo_node_subdivision_surface_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ +#ifndef WITH_OPENSUBDIV + UNUSED_VARS(ptr); + uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); +#else + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#endif +} + +static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; +} + namespace blender::nodes { static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { @@ -53,6 +75,10 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else + const NodeGeometrySubdivisionSurface &storage = + *(const NodeGeometrySubdivisionSurface *)params.node().storage; + const int uv_smooth = storage.uv_smooth; + const int boundary_smooth = storage.boundary_smooth; const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); /* Only process subdivision if level is greater than 0. */ @@ -62,8 +88,6 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) } const bool use_crease = params.extract_input<bool>("Use Creases"); - const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth"); - const bool smooth_uvs = params.extract_input<bool>("Smooth UVs"); const Mesh *mesh_in = geometry_set.get_mesh_for_read(); /* Initialize mesh settings. */ @@ -79,9 +103,9 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) subdiv_settings.level = subdiv_level; subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( - !boundary_smooth); + boundary_smooth); subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( - smooth_uvs); + uv_smooth); /* Apply subdivision to mesh. */ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); @@ -93,7 +117,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) } Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_calc_normals(mesh_out); + BKE_mesh_normals_tag_dirty(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); mesh_component.replace(mesh_out); @@ -117,5 +141,12 @@ void register_node_type_geo_subdivision_surface() node_type_socket_templates( &ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out); ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; + ntype.draw_buttons = geo_node_subdivision_surface_layout; + node_type_init(&ntype, geo_node_subdivision_surface_init); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_storage(&ntype, + "NodeGeometrySubdivisionSurface", + node_free_standard_storage, + node_copy_standard_storage); 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 403f4906d07..4c1151bf6c2 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 @@ -149,6 +149,9 @@ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) #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); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index bfd1ad02d36..a3bbca90731 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,7 +19,6 @@ #include "DEG_depsgraph_query.h" #include "NOD_geometry_exec.hh" -#include "NOD_type_callbacks.hh" #include "NOD_type_conversions.hh" #include "node_geometry_util.hh" @@ -218,7 +217,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, BLI_assert_unreachable(); } else if (requested_type != nullptr) { - const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); + const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; @@ -258,7 +257,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType BLI_assert_unreachable(); } else { - const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo); + const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); if (value_type != expected_type) { std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc new file mode 100644 index 00000000000..c91899ed8c2 --- /dev/null +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -0,0 +1,40 @@ +/* + * 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 "NOD_multi_function.hh" + +namespace blender::nodes { + +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +{ + for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { + bNodeTree *btree = tree_ref->btree(); + for (const NodeRef *node : tree_ref->nodes()) { + bNode *bnode = node->bnode(); + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + bnode->typeinfo->build_multi_function(builder); + const MultiFunction *fn = builder.built_fn_; + if (fn != nullptr) { + map_.add_new(bnode, fn); + } + } + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 8fdad0bb242..528616eb23a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,7 +44,6 @@ #include "MEM_guardedalloc.h" -#include "NOD_node_tree_multi_function.hh" #include "NOD_socket.h" #include "FN_cpp_type_make.hh" @@ -607,60 +606,74 @@ static bNodeSocketType *make_socket_type_virtual() static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<float>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<float>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<int>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<int>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { + return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); + }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -672,50 +685,60 @@ MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); }; - socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { new (r_value) GeometrySet(); }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); - socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); }; - socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc deleted file mode 100644 index 7ab6495f733..00000000000 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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 "NOD_node_tree_multi_function.hh" -#include "NOD_type_conversions.hh" - -#include "FN_multi_function_network_evaluation.hh" - -#include "BLI_color.hh" -#include "BLI_float2.hh" -#include "BLI_float3.hh" - -namespace blender::nodes { - -const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name) -{ - Vector<fn::MFDataType, 10> input_types; - Vector<fn::MFDataType, 10> output_types; - - for (const InputSocketRef *dsocket : dnode_->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - input_types.append(*data_type); - } - } - } - for (const OutputSocketRef *dsocket : dnode_->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo()); - if (data_type.has_value()) { - output_types.append(*data_type); - } - } - } - - const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>( - name, input_types, output_types); - return fn; -} - -static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) -{ - constexpr int stack_capacity = 10; - - Vector<fn::MFDataType, stack_capacity> input_types; - Vector<StringRef, stack_capacity> input_names; - Vector<const InputSocketRef *, stack_capacity> input_dsockets; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - input_types.append(*data_type); - input_names.append(dsocket->name()); - input_dsockets.append(dsocket); - } - } - } - - Vector<fn::MFDataType, stack_capacity> output_types; - Vector<StringRef, stack_capacity> output_names; - Vector<const OutputSocketRef *, stack_capacity> output_dsockets; - - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); - if (data_type.has_value()) { - output_types.append(*data_type); - output_names.append(dsocket->name()); - output_dsockets.append(dsocket); - } - } - } - - fn::MFDummyNode &dummy_node = common.network.add_dummy( - dnode->name(), input_types, output_types, input_names, output_names); - - common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs()); - common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs()); -} - -static bool has_data_sockets(const DNode &dnode) -{ - for (const InputSocketRef *socket : dnode->inputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - for (const OutputSocketRef *socket : dnode->outputs()) { - if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { - return true; - } - } - return false; -} - -static void foreach_node_to_insert(CommonMFNetworkBuilderData &common, - FunctionRef<void(DNode)> callback) -{ - common.tree.foreach_node([&](const DNode dnode) { - if (dnode->is_group_node()) { - return; - } - /* Don't insert non-root group input/output nodes, because they will be inlined. */ - if (!dnode.context()->is_root()) { - if (dnode->is_group_input_node() || dnode->is_group_output_node()) { - return; - } - } - callback(dnode); - }); -} - -/** - * Expands all function nodes in the multi-function network. Nodes that don't have an expand - * function, but do have data sockets, will get corresponding dummy nodes. - */ -static void insert_nodes(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network != nullptr) { - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - } - else if (has_data_sockets(dnode)) { - insert_dummy_node(common, dnode); - } - }); -} - -static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, - fn::MFDataType type) -{ - const fn::MultiFunction *default_fn; - if (type.is_single()) { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>( - AT, type.single_type(), type.single_type().default_value()); - } - else { - default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>( - AT, fn::GSpan(type.vector_base_type())); - } - - fn::MFNode &node = common.network.add_function(*default_fn); - return node.output(0); -} - -static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common, - const DInputSocket &dsocket) -{ - BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo())); - - SocketMFNetworkBuilder builder{common, dsocket}; - socket_expand_in_mf_network(builder); - - fn::MFOutputSocket *built_socket = builder.built_socket(); - BLI_assert(built_socket != nullptr); - return built_socket; -} - -static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common) -{ - foreach_node_to_insert(common, [&](const DNode dnode) { - for (const InputSocketRef *socket_ref : dnode->inputs()) { - const DInputSocket to_dsocket{dnode.context(), socket_ref}; - if (!to_dsocket->is_available()) { - continue; - } - if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) { - continue; - } - - Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(to_dsocket); - BLI_assert(to_sockets.size() >= 1); - const fn::MFDataType to_type = to_sockets[0]->data_type(); - - Vector<DSocket> from_dsockets; - to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); }); - if (from_dsockets.size() > 1) { - fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(from_socket, *to_socket); - } - continue; - } - if (from_dsockets.is_empty()) { - /* The socket is not linked. Need to use the value of the socket itself. */ - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - if (from_dsockets[0]->is_input()) { - DInputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket); - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*built_socket, *to_socket); - } - continue; - } - DOutputSocket from_dsocket{from_dsockets[0]}; - fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket); - const fn::MFDataType from_type = from_socket->data_type(); - - if (from_type != to_type) { - const fn::MultiFunction *conversion_fn = - get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type); - if (conversion_fn != nullptr) { - fn::MFNode &node = common.network.add_function(*conversion_fn); - common.network.add_link(*from_socket, node.input(0)); - from_socket = &node.output(0); - } - else { - from_socket = &insert_default_value_for_type(common, to_type); - } - } - - for (fn::MFInputSocket *to_socket : to_sockets) { - common.network.add_link(*from_socket, *to_socket); - } - } - }); -} - -/** - * Expands all function nodes contained in the given node tree within the given multi-function - * network. - * - * Returns a mapping between the original node tree and the generated nodes/sockets for further - * processing. - */ -MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, - const DerivedNodeTree &tree, - ResourceScope &scope) -{ - MFNetworkTreeMap network_map{tree, network}; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - insert_nodes(common); - insert_links_and_unlinked_inputs(common); - - return network_map; -} - -/** - * A single node is allowed to expand into multiple nodes before evaluation. Depending on what - * nodes it expands to, it belongs a different type of the ones below. - */ -enum class NodeExpandType { - SingleFunctionNode, - MultipleFunctionNodes, - HasDummyNodes, -}; - -/** - * Checks how the given node expanded in the multi-function network. If it is only a single - * function node, the corresponding function is returned as well. - */ -static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map, - const DNode &dnode, - const fn::MultiFunction **r_single_function) -{ - const fn::MFFunctionNode *single_function_node = nullptr; - bool has_multiple_nodes = false; - bool has_dummy_nodes = false; - - auto check_mf_node = [&](fn::MFNode &mf_node) { - if (mf_node.is_function()) { - if (single_function_node == nullptr) { - single_function_node = &mf_node.as_function(); - } - if (&mf_node != single_function_node) { - has_multiple_nodes = true; - } - } - else { - BLI_assert(mf_node.is_dummy()); - has_dummy_nodes = true; - } - }; - - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - check_mf_node(mf_input->node()); - } - } - } - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - check_mf_node(mf_output.node()); - } - } - - if (has_dummy_nodes) { - return NodeExpandType::HasDummyNodes; - } - if (has_multiple_nodes) { - return NodeExpandType::MultipleFunctionNodes; - } - *r_single_function = &single_function_node->function(); - return NodeExpandType::SingleFunctionNode; -} - -static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple( - const DNode &dnode, - fn::MFNetwork &network, - MFNetworkTreeMap &network_map, - ResourceScope &scope) -{ - Vector<const fn::MFOutputSocket *> dummy_fn_inputs; - for (const InputSocketRef *dsocket : dnode->inputs()) { - if (dsocket->is_available()) { - MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo()); - fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type); - for (fn::MFInputSocket *mf_input : - network_map.lookup(DInputSocket(dnode.context(), dsocket))) { - network.add_link(fn_input, *mf_input); - dummy_fn_inputs.append(&fn_input); - } - } - } - Vector<const fn::MFInputSocket *> dummy_fn_outputs; - for (const OutputSocketRef *dsocket : dnode->outputs()) { - if (dsocket->is_available()) { - fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket)); - MFDataType data_type = mf_output.data_type(); - fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type); - network.add_link(mf_output, fn_output); - dummy_fn_outputs.append(&fn_output); - } - } - - fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>( - __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs)); - return fn_evaluator; -} - -/** - * Returns a single multi-function for every node that supports it. This makes it easier to reuse - * the multi-function implementation of nodes in different contexts. - */ -MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope) -{ - /* Build a network that nodes can insert themselves into. However, the individual nodes are not - * connected. */ - fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__); - MFNetworkTreeMap network_map{tree, network}; - MultiFunctionByNode functions_by_node; - - CommonMFNetworkBuilderData common{scope, network, network_map, tree}; - - tree.foreach_node([&](DNode dnode) { - const bNodeType *node_type = dnode->typeinfo(); - if (node_type->expand_in_mf_network == nullptr) { - /* This node does not have a multi-function implementation. */ - return; - } - - NodeMFNetworkBuilder builder{common, dnode}; - node_type->expand_in_mf_network(builder); - - const fn::MultiFunction *single_function = nullptr; - const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function); - - switch (expand_type) { - case NodeExpandType::HasDummyNodes: { - /* Dummy nodes cannot be executed, so skip them. */ - break; - } - case NodeExpandType::SingleFunctionNode: { - /* This is the common case. Most nodes just expand to a single function. */ - functions_by_node.add_new(dnode, single_function); - break; - } - case NodeExpandType::MultipleFunctionNodes: { - /* If a node expanded into multiple functions, a new function has to be created that - * combines those. */ - const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple( - dnode, network, network_map, scope); - functions_by_node.add_new(dnode, &fn); - break; - } - } - }); - - return functions_by_node; -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc deleted file mode 100644 index 5160432aea5..00000000000 --- a/source/blender/nodes/intern/type_callbacks.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 "NOD_node_tree_multi_function.hh" -#include "NOD_type_callbacks.hh" - -namespace blender::nodes { - -const CPPType *socket_cpp_type_get(const bNodeSocketType &stype) -{ - if (stype.get_cpp_type != nullptr) { - return stype.get_cpp_type(); - } - return nullptr; -} - -std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype) -{ - const CPPType *cpp_type = socket_cpp_type_get(stype); - if (cpp_type != nullptr) { - return MFDataType::ForSingle(*cpp_type); - } - return {}; -} - -bool socket_is_mf_data_socket(const bNodeSocketType &stype) -{ - if (!socket_mf_type_get(stype).has_value()) { - return false; - } - if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) { - return false; - } - return true; -} - -bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value) -{ - if (socket.typeinfo->get_cpp_value != nullptr) { - socket.typeinfo->get_cpp_value(socket, r_value); - return true; - } - return false; -} - -void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder) -{ - bNodeSocket &socket = builder.bsocket(); - if (socket.typeinfo->expand_in_mf_network != nullptr) { - socket.typeinfo->expand_in_mf_network(builder); - } - else if (socket.typeinfo->get_cpp_value != nullptr) { - const CPPType &type = *socket_cpp_type_get(*socket.typeinfo); - void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(), - type.alignment()); - socket.typeinfo->get_cpp_value(socket, buffer); - builder.set_constant_value(type, buffer); - } - else { - BLI_assert_unreachable(); - } -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 7367f73d171..98edc133a1d 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -531,6 +531,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) bNodeTree *ngroup = (bNodeTree *)node->id; ntreeFreeLocalNode(localtree, node); ntreeFreeTree(ngroup); + BLI_assert(!ngroup->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(ngroup); } else { diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index dc44f0fa98f..a75354d3381 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -72,7 +72,7 @@ #ifdef __cplusplus # include "FN_multi_function_builder.hh" -# include "NOD_node_tree_multi_function.hh" +# include "NOD_multi_function.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 4f77421cfe0..f105f8bcaf9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat, GPU_stack_link(mat, node, "clamp_range", in, out); } -static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{ "Clamp (Min Max)", @@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil return clamp_f(value, b, a); }}; - int clamp_type = builder.bnode().custom1; + int clamp_type = builder.node().custom1; if (clamp_type == NODE_CLAMP_MINMAX) { builder.set_matching_fn(minmax_fn); } @@ -82,7 +82,7 @@ void register_node_type_sh_clamp(void) node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); - ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network; + ntype.build_multi_function = sh_node_clamp_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index f1d5040a292..df075d6e973 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_vec_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); @@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); node_type_gpu(&ntype, gpu_shader_curve_vec); - ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_vec_build_multi_function; nodeRegisterType(&ntype); } @@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_curve_rgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); @@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void) node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); node_type_gpu(&ntype, gpu_shader_curve_rgb); - ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_curve_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c index 56169b9be5e..015af19abb2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c +++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c @@ -91,7 +91,7 @@ void register_node_type_sh_eevee_specular(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular", NODE_CLASS_SHADER, 0); + sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular BSDF", NODE_CLASS_SHADER, 0); node_type_socket_templates(&ntype, sh_node_eevee_specular_in, sh_node_eevee_specular_out); node_type_init(&ntype, NULL); node_type_storage(&ntype, "", NULL, NULL); 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 ad7abd9d491..e4739e2864d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { } }; -static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_map_range_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); bool clamp = bnode.custom1 != 0; int interpolation_type = bnode.custom2; @@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork break; } default: - builder.set_not_implemented(); break; } } @@ -315,7 +315,7 @@ void register_node_type_sh_map_range(void) node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); - ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network; + ntype.build_multi_function = sh_node_map_range_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 7a846031456..c30f2948ab1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const blender::fn::MultiFunction &get_base_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) { - const int mode = builder.bnode().custom1; - + const int mode = node.custom1; const blender::fn::MultiFunction *base_fn = nullptr; blender::nodes::try_dispatch_float_math_fl_to_fl( @@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_to_fl( @@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( @@ -102,36 +100,51 @@ static const blender::fn::MultiFunction &get_base_multi_function( base_fn = &fn; }); if (base_fn != nullptr) { - return *base_fn; + return base_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) -{ - const blender::fn::MultiFunction &base_function = get_base_multi_function(builder); +class ClampWrapperFunction : public blender::fn::MultiFunction { + private: + const blender::fn::MultiFunction &fn_; - const blender::nodes::DNode &dnode = builder.dnode(); - blender::fn::MFNetwork &network = builder.network(); - blender::fn::MFFunctionNode &base_node = network.add_function(base_function); + public: + ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn) + { + this->set_signature(&fn.signature()); + } - builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs()); + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext context) const override + { + fn_.call(mask, params, context); + + /* Assumes the output parameter is the last one. */ + const int output_param_index = this->param_amount() - 1; + /* This has actually been initialized in the call above. */ + blender::MutableSpan<float> results = params.uninitialized_single_output<float>( + output_param_index); + + for (const int i : mask) { + float &value = results[i]; + CLAMP(value, 0.0f, 1.0f); + } + } +}; + +static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node()); - const bool clamp_output = builder.bnode().custom2 != 0; + const bool clamp_output = builder.node().custom2 != 0; if (clamp_output) { - static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) { - CLAMP(value, 0.0f, 1.0f); - return value; - }}; - blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn); - network.add_link(base_node.output(0), clamp_node.input(0)); - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - clamp_node.output(0)); + builder.construct_and_set_matching_fn<ClampWrapperFunction>(*base_function); } else { - builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)), - base_node.output(0)); + builder.set_matching_fn(base_function); } } @@ -144,7 +157,7 @@ void register_node_type_sh_math(void) node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); - ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_math_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 6baaa17f956..ade35a40366 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -127,15 +127,71 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat, return 0; } +class MixRGBFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + int type_; + + public: + MixRGBFunction(bool clamp, int type) : clamp_(clamp), type_(type) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"MixRGB"}; + signature.single_input<float>("Fac"); + signature.single_input<blender::ColorGeometry4f>("Color1"); + signature.single_input<blender::ColorGeometry4f>("Color2"); + signature.single_output<blender::ColorGeometry4f>("Color"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac"); + const blender::VArray<blender::ColorGeometry4f> &col1 = + params.readonly_single_input<blender::ColorGeometry4f>(1, "Color1"); + const blender::VArray<blender::ColorGeometry4f> &col2 = + params.readonly_single_input<blender::ColorGeometry4f>(2, "Color2"); + blender::MutableSpan<blender::ColorGeometry4f> results = + params.uninitialized_single_output<blender::ColorGeometry4f>(3, "Color"); + + for (int64_t i : mask) { + results[i] = col1[i]; + ramp_blend(type_, results[i], clamp_f(fac[i], 0.0f, 1.0f), col2[i]); + } + + if (clamp_) { + for (int64_t i : mask) { + clamp_v3(results[i], 0.0f, 1.0f); + } + } + } +}; + +static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; + int mix_type = node.custom1; + builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type); +} + void register_node_type_sh_mix_rgb(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); + sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out); node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); + ntype.build_multi_function = sh_node_mix_rgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index a7239154633..2779fc6bf68 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction { } }; -static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static SeparateRGBFunction fn; builder.set_matching_fn(fn); @@ -110,7 +110,7 @@ void register_node_type_sh_seprgb(void) node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); - ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_seprgb_build_multi_function; nodeRegisterType(&ntype); } @@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_rgb", in, out); } -static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{ "Combine RGB", @@ -169,7 +169,7 @@ void register_node_type_sh_combrgb(void) node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); - ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_combrgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index efa9581c414..1fd794cdd0a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction { } }; -static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static MF_SeparateXYZ separate_fn; builder.set_matching_fn(separate_fn); @@ -94,7 +94,7 @@ void register_node_type_sh_sepxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); node_type_gpu(&ntype, gpu_shader_sepxyz); - ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } @@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } -static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{ "Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }}; @@ -134,7 +134,7 @@ void register_node_type_sh_combxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); node_type_gpu(&ntype, gpu_shader_combxyz); - ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network; + ntype.build_multi_function = sh_node_combxyz_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 5b2eb300aac..1bc42ab0cc6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction { } }; -static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_valtorgb_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.bnode(); + bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band); } @@ -181,7 +182,7 @@ void register_node_type_sh_valtorgb(void) node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb); node_type_gpu(&ntype, gpu_shader_valtorgb); - ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network; + ntype.build_multi_function = sh_node_valtorgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 495c8d12824..602d5a1cf56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } -static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket(); + const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first; const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); } @@ -53,7 +53,7 @@ void register_node_type_sh_value(void) sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); node_type_socket_templates(&ntype, nullptr, sh_node_value_out); node_type_gpu(&ntype, gpu_shader_value); - ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network; + ntype.build_multi_function = sh_node_value_build_multi_function; nodeRegisterType(&ntype); } 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 419a11201aa..4424db6aed1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node } } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { using blender::float3; - NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1); + NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); const blender::fn::MultiFunction *multi_fn = nullptr; @@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( @@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( @@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( @@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( @@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl3( @@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } blender::nodes::try_dispatch_float_math_fl3_to_fl( @@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function( multi_fn = &fn; }); if (multi_fn != nullptr) { - return *multi_fn; + return multi_fn; } - return builder.get_not_implemented_fn(); + return nullptr; } -static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_math_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void) node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); - ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_math_build_multi_function; nodeRegisterType(&ntype); } 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 3b2c2fa5a03..bc51b7e29ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector, return result + center; } -static const blender::fn::MultiFunction &get_multi_function( - blender::nodes::NodeMFNetworkBuilder &builder) +static const blender::fn::MultiFunction *get_multi_function(bNode &node) { - bool invert = builder.bnode().custom2; - const int mode = builder.bnode().custom1; + bool invert = node.custom2; + const int mode = node.custom1; switch (mode) { case NODE_VECTOR_ROTATE_TYPE_AXIS: { @@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{ "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_X: { float3 axis = float3(1.0f, 0.0f, 0.0f); @@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate X-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: { float3 axis = float3(0.0f, 1.0f, 0.0f); @@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Y-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: { float3 axis = float3(0.0f, 0.0f, 1.0f); @@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, -angle); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ "Rotate Z-Axis", [=](float3 in, float3 center, float angle) { return sh_node_vector_rotate_around_axis(in, center, axis, angle); }}; - return fn; + return &fn; } case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: { if (invert) { @@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function( "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, true); }}; - return fn; + return &fn; } static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ "Rotate Euler", [](float3 in, float3 center, float3 rotation) { return sh_node_vector_rotate_euler(in, center, rotation, false); }}; - return fn; + return &fn; } default: BLI_assert_unreachable(); - return builder.get_not_implemented_fn(); + return nullptr; } } -static void sh_node_vector_rotate_expand_in_mf_network( - blender::nodes::NodeMFNetworkBuilder &builder) +static void sh_node_vector_rotate_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction &fn = get_multi_function(builder); + const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } @@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void) node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); - ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network; + ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 612446e6d19..e5e601e0eb6 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -3241,9 +3241,11 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) const Py_ssize_t len = bpy_bmelemseq_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index ff06cf43026..0aa92158524 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -843,9 +843,11 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py const Py_ssize_t len = bpy_bmlayercollection_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c index 8f4e07c30d3..d0c745e6a1d 100644 --- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c @@ -443,7 +443,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, } if (value) { - /* dvert[group_index] = 0.5 */ + /* Handle `dvert[group_index] = 0.5`. */ if (i < 0) { PyErr_SetString(PyExc_KeyError, "BMDeformVert[key] = x: " @@ -453,7 +453,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, MDeformWeight *dw = BKE_defvert_ensure_index(self->data, i); const float f = PyFloat_AsDouble(value); - if (f == -1 && PyErr_Occurred()) { // parsed key not a number + if (f == -1 && PyErr_Occurred()) { /* Parsed key not a number. */ PyErr_SetString(PyExc_TypeError, "BMDeformVert[key] = x: " "assigned value not a number"); @@ -463,7 +463,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, dw->weight = clamp_f(f, 0.0f, 1.0f); } else { - /* del dvert[group_index] */ + /* Handle `del dvert[group_index]`. */ MDeformWeight *dw = BKE_defvert_find_index(self->data, i); if (dw == NULL) { diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c index 9bb9815f731..b89822a080c 100644 --- a/source/blender/python/bmesh/bmesh_py_types_select.c +++ b/source/blender/python/bmesh/bmesh_py_types_select.c @@ -205,7 +205,6 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, Py_ssize_t stop) { int count = 0; - bool ok; PyObject *list; BMEditSelection *ese; @@ -214,30 +213,22 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self, list = PyList_New(0); - ese = self->bm->selected.first; - - ok = (ese != NULL); - - if (UNLIKELY(ok == false)) { - return list; - } - - /* first loop up-until the start */ - for (ok = true; ok; ok = ((ese = ese->next) != NULL)) { + /* First loop up-until the start. */ + for (ese = self->bm->selected.first; ese; ese = ese->next) { if (count == start) { break; } count++; } - /* add items until stop */ - do { + /* Add items until stop. */ + for (; ese; ese = ese->next) { PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head)); count++; if (count == stop) { break; } - } while ((ese = ese->next)); + } return list; } @@ -282,9 +273,11 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke const Py_ssize_t len = bpy_bmeditselseq_length(self); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c index 0ec66e22fa9..9e725730d40 100644 --- a/source/blender/python/generic/blf_py_api.c +++ b/source/blender/python/generic/blf_py_api.c @@ -493,7 +493,6 @@ PyObject *BPyInit_blf(void) PyModule_AddIntConstant(submodule, "ROTATION", BLF_ROTATION); PyModule_AddIntConstant(submodule, "CLIPPING", BLF_CLIPPING); PyModule_AddIntConstant(submodule, "SHADOW", BLF_SHADOW); - PyModule_AddIntConstant(submodule, "KERNING_DEFAULT", BLF_KERNING_DEFAULT); PyModule_AddIntConstant(submodule, "WORD_WRAP", BLF_WORD_WRAP); PyModule_AddIntConstant(submodule, "MONOCHROME", BLF_MONOCHROME); diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 1591413530c..bfe0e66e393 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -42,13 +42,13 @@ void PyC_Err_PrintWithFunc(PyObject *py_func); void PyC_FileAndNum(const char **r_filename, int *r_lineno); void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); /* checks python is running */ int PyC_AsArray_FAST(void *array, - const size_t array_elem_size, + const size_t array_item_size, PyObject *value_fast, const Py_ssize_t length, const PyTypeObject *type, const char *error_prefix); int PyC_AsArray(void *array, - const size_t array_elem_size, + const size_t array_item_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index a1fc89e772e..0fef59d6352 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -37,7 +37,7 @@ #include "gpu_py_buffer.h" -//#define PYGPU_BUFFER_PROTOCOL +#define PYGPU_BUFFER_PROTOCOL #define MAX_DIMENSIONS 64 /* -------------------------------------------------------------------- */ @@ -608,7 +608,7 @@ static void pygpu_buffer_strides_calc(const eGPUDataFormat format, } /* Here is the buffer interface function */ -static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags) +static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int UNUSED(flags)) { if (view == NULL) { PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer"); @@ -620,7 +620,7 @@ static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int f view->len = bpygpu_Buffer_size(self); view->readonly = 0; view->itemsize = GPU_texture_dataformat_size(self->format); - view->format = pygpu_buffer_formatstr(self->format); + view->format = (char *)pygpu_buffer_formatstr(self->format); view->ndim = self->shape_len; view->shape = self->shape; view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides"); diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c index f3fb93021b2..11e7d48f096 100644 --- a/source/blender/python/gpu/gpu_py_capabilities.c +++ b/source/blender/python/gpu/gpu_py_capabilities.c @@ -33,66 +33,166 @@ /** \name Functions * \{ */ +PyDoc_STRVAR(pygpu_max_texture_size_get_doc, + ".. function:: max_texture_size_get()\n" + "\n" + " Get estimated maximum texture size to be able to handle.\n" + "\n" + " :return: Texture size.\n" + " :rtype: int\n"); static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_texture_size()); } +PyDoc_STRVAR(pygpu_max_texture_layers_get_doc, + ".. function:: max_texture_layers_get()\n" + "\n" + " Get maximum number of layers in texture.\n" + "\n" + " :return: Number of layers.\n" + " :rtype: int\n"); static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_texture_layers()); } +PyDoc_STRVAR(pygpu_max_textures_get_doc, + ".. function:: max_textures_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the vertex shader and the\n" + " fragment processor.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures()); } +PyDoc_STRVAR(pygpu_max_textures_vert_get_doc, + ".. function:: max_textures_vert_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the vertex shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_vert()); } +PyDoc_STRVAR(pygpu_max_textures_geom_get_doc, + ".. function:: max_textures_geom_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the geometry shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_geom()); } +PyDoc_STRVAR(pygpu_max_textures_frag_get_doc, + ".. function:: max_textures_frag_get()\n" + "\n" + " Get maximum supported texture image units used for\n" + " accessing texture maps from the fragment shader.\n" + "\n" + " :return: Texture image units.\n" + " :rtype: int\n"); static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_textures_frag()); } +PyDoc_STRVAR(pygpu_max_uniforms_vert_get_doc, + ".. function:: max_uniforms_vert_get()\n" + "\n" + " Get maximum number of values held in uniform variable\n" + " storage for a vertex shader.\n" + "\n" + " :return: Number of values.\n" + " :rtype: int\n"); static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_uniforms_vert()); } +PyDoc_STRVAR(pygpu_max_uniforms_frag_get_doc, + ".. function:: max_uniforms_frag_get()\n" + "\n" + " Get maximum number of values held in uniform variable\n" + " storage for a fragment shader.\n" + "\n" + " :return: Number of values.\n" + " :rtype: int\n"); static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_uniforms_frag()); } +PyDoc_STRVAR(pygpu_max_batch_indices_get_doc, + ".. function:: max_batch_indices_get()\n" + "\n" + " Get maximum number of vertex array indices.\n" + "\n" + " :return: Number of indices.\n" + " :rtype: int\n"); static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_batch_indices()); } +PyDoc_STRVAR(pygpu_max_batch_vertices_get_doc, + ".. function:: max_batch_vertices_get()\n" + "\n" + " Get maximum number of vertex array vertices.\n" + "\n" + " :return: Number of vertices.\n" + " :rtype: int\n"); static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_batch_vertices()); } +PyDoc_STRVAR(pygpu_max_vertex_attribs_get_doc, + ".. function:: max_vertex_attribs_get()\n" + "\n" + " Get maximum number of vertex attributes accessible to\n" + " a vertex shader.\n" + "\n" + " :return: Number of attributes.\n" + " :rtype: int\n"); static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_vertex_attribs()); } +PyDoc_STRVAR(pygpu_max_varying_floats_get_doc, + ".. function:: max_varying_floats_get()\n" + "\n" + " Get maximum number of varying variables used by\n" + " vertex and fragment shaders.\n" + "\n" + " :return: Number of variables.\n" + " :rtype: int\n"); static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self)) { return PyLong_FromLong(GPU_max_varying_floats()); } +PyDoc_STRVAR(pygpu_extensions_get_doc, + ".. function:: extensions_get()\n" + "\n" + " Get supported extensions in the current context.\n" + "\n" + " :return: Extensions.\n" + " :rtype: tuple of string\n"); static PyObject *pygpu_extensions_get(PyObject *UNUSED(self)) { int extensions_len = GPU_extensions_len(); @@ -112,19 +212,55 @@ static PyObject *pygpu_extensions_get(PyObject *UNUSED(self)) * \{ */ static struct PyMethodDef pygpu_capabilities__tp_methods[] = { - {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL}, - {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL}, - {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL}, - {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL}, - {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL}, - {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL}, - {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL}, - {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL}, - {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL}, - {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL}, - {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL}, - {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL}, - {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL}, + {"max_texture_size_get", + (PyCFunction)pygpu_max_texture_size_get, + METH_NOARGS, + pygpu_max_texture_size_get_doc}, + {"max_texture_layers_get", + (PyCFunction)pygpu_max_texture_layers_get, + METH_NOARGS, + pygpu_max_texture_layers_get_doc}, + {"max_textures_get", + (PyCFunction)pygpu_max_textures_get, + METH_NOARGS, + pygpu_max_textures_get_doc}, + {"max_textures_vert_get", + (PyCFunction)pygpu_max_textures_vert_get, + METH_NOARGS, + pygpu_max_textures_vert_get_doc}, + {"max_textures_geom_get", + (PyCFunction)pygpu_max_textures_geom_get, + METH_NOARGS, + pygpu_max_textures_geom_get_doc}, + {"max_textures_frag_get", + (PyCFunction)pygpu_max_textures_frag_get, + METH_NOARGS, + pygpu_max_textures_frag_get_doc}, + {"max_uniforms_vert_get", + (PyCFunction)pygpu_max_uniforms_vert_get, + METH_NOARGS, + pygpu_max_uniforms_vert_get_doc}, + {"max_uniforms_frag_get", + (PyCFunction)pygpu_max_uniforms_frag_get, + METH_NOARGS, + pygpu_max_uniforms_frag_get_doc}, + {"max_batch_indices_get", + (PyCFunction)pygpu_max_batch_indices_get, + METH_NOARGS, + pygpu_max_batch_indices_get_doc}, + {"max_batch_vertices_get", + (PyCFunction)pygpu_max_batch_vertices_get, + METH_NOARGS, + pygpu_max_batch_vertices_get_doc}, + {"max_vertex_attribs_get", + (PyCFunction)pygpu_max_vertex_attribs_get, + METH_NOARGS, + pygpu_max_vertex_attribs_get_doc}, + {"max_varying_floats_get", + (PyCFunction)pygpu_max_varying_floats_get, + METH_NOARGS, + pygpu_max_varying_floats_get_doc}, + {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, pygpu_extensions_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 457f00b1267..6f23c2213e2 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -227,7 +227,7 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self), } if (GPU_context_active_get()) { - ofs = GPU_offscreen_create(width, height, true, false, err_out); + ofs = GPU_offscreen_create(width, height, true, GPU_RGBA8, err_out); } else { STRNCPY(err_out, "No active GPU context found"); @@ -279,7 +279,8 @@ static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void * PyDoc_STRVAR( pygpu_offscreen_draw_view3d_doc, - ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n" + ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, " + "do_color_management=False)\n" "\n" " Draw the 3d viewport in the offscreen object.\n" "\n" @@ -294,7 +295,9 @@ PyDoc_STRVAR( " :arg view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n" " :type view_matrix: :class:`mathutils.Matrix`\n" " :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n" - " :type projection_matrix: :class:`mathutils.Matrix`\n"); + " :type projection_matrix: :class:`mathutils.Matrix`\n" + " :arg do_color_management: Color manage the output.\n" + " :type do_color_management: bool\n"); static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds) { MatrixObject *py_mat_view, *py_mat_projection; @@ -306,12 +309,20 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar View3D *v3d; ARegion *region; + bool do_color_management = false; + BPY_GPU_OFFSCREEN_CHECK_OBJ(self); - static const char *_keywords[] = { - "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", NULL}; + static const char *_keywords[] = {"scene", + "view_layer", + "view3d", + "region", + "view_matrix", + "projection_matrix", + "do_color_management", + NULL}; - static _PyArg_Parser _parser = {"OOOOO&O&:draw_view3d", _keywords, 0}; + static _PyArg_Parser _parser = {"OOOOO&O&|$O&:draw_view3d", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, @@ -322,7 +333,9 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar Matrix_Parse4x4, &py_mat_view, Matrix_Parse4x4, - &py_mat_projection) || + &py_mat_projection, + PyC_ParseBool, + &do_color_management) || (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) || !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) || !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) || @@ -354,7 +367,7 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar true, true, "", - false, + do_color_management, true, self->ofs, NULL); diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index 132052b6f1d..62310a83642 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -33,16 +33,37 @@ /** \name Functions * \{ */ +PyDoc_STRVAR(pygpu_platform_vendor_get_doc, + ".. function:: vendor_get()\n" + "\n" + " Get GPU vendor.\n" + "\n" + " :return: Vendor name.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_vendor()); } +PyDoc_STRVAR(pygpu_platform_renderer_get_doc, + ".. function:: renderer_get()\n" + "\n" + " Get GPU to be used for rendering.\n" + "\n" + " :return: GPU name.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_renderer()); } +PyDoc_STRVAR(pygpu_platform_version_get_doc, + ".. function:: version_get()\n" + "\n" + " Get GPU driver version.\n" + "\n" + " :return: Driver version.\n" + " :rtype: str\n"); static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) { return PyUnicode_FromString(GPU_platform_version()); @@ -55,9 +76,18 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) * \{ */ static struct PyMethodDef pygpu_platform__tp_methods[] = { - {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL}, - {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL}, - {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL}, + {"vendor_get", + (PyCFunction)pygpu_platform_vendor_get, + METH_NOARGS, + pygpu_platform_vendor_get_doc}, + {"renderer_get", + (PyCFunction)pygpu_platform_renderer_get, + METH_NOARGS, + pygpu_platform_renderer_get_doc}, + {"version_get", + (PyCFunction)pygpu_platform_version_get, + METH_NOARGS, + pygpu_platform_version_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index b3f1c186716..145586d8ab0 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -44,14 +44,28 @@ /** \name Enum Conversion. * \{ */ +#define PYDOC_BUILTIN_SHADER_LIST \ + " - ``2D_FLAT_COLOR``\n" \ + " - ``2D_IMAGE``\n" \ + " - ``2D_SMOOTH_COLOR``\n" \ + " - ``2D_UNIFORM_COLOR``\n" \ + " - ``3D_FLAT_COLOR``\n" \ + " - ``3D_SMOOTH_COLOR``\n" \ + " - ``3D_UNIFORM_COLOR``\n" \ + " - ``3D_POLYLINE_FLAT_COLOR``\n" \ + " - ``3D_POLYLINE_SMOOTH_COLOR``\n" \ + " - ``3D_POLYLINE_UNIFORM_COLOR``\n" + static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = { - {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"}, - {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, {GPU_SHADER_2D_IMAGE, "2D_IMAGE"}, - {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, + {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"}, + {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"}, {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"}, {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"}, + {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"}, + {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"}, + {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "3D_POLYLINE_SMOOTH_COLOR"}, {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"}, {0, NULL}, }; @@ -197,8 +211,9 @@ static bool pygpu_shader_uniform_vector_impl(PyObject *args, return false; } - if (r_pybuffer->len != (*r_length * *r_count * elem_size)) { - PyErr_SetString(PyExc_BufferError, "GPUShader.uniform_vector_*: buffer size does not match."); + if (r_pybuffer->len < (*r_length * *r_count * elem_size)) { + PyErr_SetString(PyExc_OverflowError, + "GPUShader.uniform_vector_*: buffer size smaller than required."); return false; } @@ -704,14 +719,8 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc, " For more details, you can check the shader code with the\n" " :func:`gpu.shader.code_from_builtin` function.\n" "\n" - " :param pygpu_shader_name: One of these builtin shader names:\n\n" - " - ``2D_UNIFORM_COLOR``\n" - " - ``2D_FLAT_COLOR``\n" - " - ``2D_SMOOTH_COLOR``\n" - " - ``2D_IMAGE``\n" - " - ``3D_UNIFORM_COLOR``\n" - " - ``3D_FLAT_COLOR``\n" - " - ``3D_SMOOTH_COLOR``\n" + " :param pygpu_shader_name: One of these builtin shader names:\n" + "\n" PYDOC_BUILTIN_SHADER_LIST " :type pygpu_shader_name: str\n" " :return: Shader object corresponding to the given name.\n" " :rtype: :class:`bpy.types.GPUShader`\n"); @@ -734,14 +743,8 @@ PyDoc_STRVAR(pygpu_shader_code_from_builtin_doc, "\n" " Exposes the internal shader code for query.\n" "\n" - " :param pygpu_shader_name: One of these builtin shader names:\n\n" - " - ``2D_UNIFORM_COLOR``\n" - " - ``2D_FLAT_COLOR``\n" - " - ``2D_SMOOTH_COLOR``\n" - " - ``2D_IMAGE``\n" - " - ``3D_UNIFORM_COLOR``\n" - " - ``3D_FLAT_COLOR``\n" - " - ``3D_SMOOTH_COLOR``\n" + " :param pygpu_shader_name: One of these builtin shader names:\n" + "\n" PYDOC_BUILTIN_SHADER_LIST " :type pygpu_shader_name: str\n" " :return: Vertex, fragment and geometry shader codes.\n" " :rtype: dict\n"); diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 30a61067c9e..0e0f431cb19 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -417,9 +417,6 @@ void BPy_init_modules(struct bContext *C) PyDict_SetItemString(PyImport_GetModuleDict(), "_bpy", mod); Py_DECREF(mod); - /* run first, initializes rna types */ - BPY_rna_init(); - /* needs to be first so bpy_types can run */ PyModule_AddObject(mod, "types", BPY_rna_types()); diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 7cca3ae4700..acd809fb8d5 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -35,7 +35,7 @@ /* We may want to load direct from file. */ PyDoc_STRVAR( bpy_app_icons_new_triangles_doc, - ".. function:: new_triangles(range, coords, colors)" + ".. function:: new_triangles(range, coords, colors)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -91,7 +91,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a } PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc, - ".. function:: new_triangles_from_file(filename)" + ".. function:: new_triangles_from_file(filename)\n" "\n" " Create a new icon from triangle geometry.\n" "\n" @@ -122,7 +122,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), } PyDoc_STRVAR(bpy_app_icons_release_doc, - ".. function:: release(icon_id)" + ".. function:: release(icon_id)\n" "\n" " Release the icon.\n"); static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw) diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index 7437598582f..de70035eb2b 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -746,7 +746,7 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kw)) { - /* printf("%s (%p)\n", __func__, _translations); */ + // printf("%s (%p)\n", __func__, _translations); if (!_translations) { _translations = (BlenderAppTranslations *)type->tp_alloc(type, 0); diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c index 42e0c7d0003..a05ec6b7000 100644 --- a/source/blender/python/intern/bpy_gizmo_wrap.c +++ b/source/blender/python/intern/bpy_gizmo_wrap.c @@ -79,7 +79,7 @@ static bool bpy_gizmotype_target_property_def(wmGizmoType *gzt, PyObject *item) goto fail; } - if ((params.array_length < 1 || params.array_length > RNA_MAX_ARRAY_LENGTH)) { + if ((params.array_length < 1) || (params.array_length > RNA_MAX_ARRAY_LENGTH)) { PyErr_SetString(PyExc_ValueError, "'array_length' out of range"); goto fail; } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index f91ba4d362c..1a308414bc3 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -155,7 +155,7 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) } else if (py_call_level == 0) { /* XXX: Calling classes currently won't store the context :\, - * can't set NULL because of this. but this is very flakey still. */ + * can't set NULL because of this. but this is very flaky still. */ #if 0 BPY_context_set(NULL); #endif @@ -502,7 +502,10 @@ void BPY_python_start(bContext *C, int argc, const char **argv) } #endif - /* bpy.* and lets us import it */ + /* Run first, initializes RNA types. */ + BPY_rna_init(); + + /* Defines `bpy.*` and lets us import it. */ BPy_init_modules(C); pyrna_alloc_types(); @@ -541,6 +544,8 @@ void BPY_python_end(void) /* free other python data. */ pyrna_free_types(); + BPY_rna_exit(); + /* clear all python data from structs */ bpy_intern_string_exit(); @@ -650,7 +655,7 @@ void BPY_modules_load_user(bContext *C) bpy_context_set(C, &gilstate); for (text = bmain->texts.first; text; text = text->id.next) { - if (text->flags & TXT_ISSCRIPT && BLI_path_extension_check(text->id.name + 2, ".py")) { + if (text->flags & TXT_ISSCRIPT) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL; diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index dff96f74d62..ac1a7f68885 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -181,23 +181,13 @@ static PyMethodDef id_free_weakref_cb_def = { /* Adds a reference to the list, remember to decref. */ static GHash *id_weakref_pool_get(ID *id) { - GHash *weakinfo_hash = NULL; - - if (id_weakref_pool) { - weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id); - } - else { - /* First time, allocate pool. */ - id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool"); - weakinfo_hash = NULL; - } - + GHash *weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id); if (weakinfo_hash == NULL) { - /* We use a ghash as a set, we could use libHX's HXMAP_SINGULAR, but would be an extra dep. */ + /* This could be a set, values are used to keep a reference back to the ID + * (all of them are the same). */ weakinfo_hash = BLI_ghash_ptr_new("rna_id"); BLI_ghash_insert(id_weakref_pool, id, weakinfo_hash); } - return weakinfo_hash; } @@ -283,14 +273,6 @@ static void id_release_weakref_list(struct ID *id, GHash *weakinfo_hash) BLI_ghash_remove(id_weakref_pool, (void *)id, NULL, NULL); BLI_ghash_free(weakinfo_hash, NULL, NULL); - - if (BLI_ghash_len(id_weakref_pool) == 0) { - BLI_ghash_free(id_weakref_pool, NULL, NULL); - id_weakref_pool = NULL; -# ifdef DEBUG_RNA_WEAKREF - printf("id_release_weakref freeing pool\n"); -# endif - } } static void id_release_weakref(struct ID *id) @@ -310,7 +292,8 @@ void BPY_id_release(struct ID *id) #endif #ifdef USE_PYRNA_INVALIDATE_WEAKREF - if (id_weakref_pool) { + /* Check for NULL since this may run before Python has been started. */ + if (id_weakref_pool != NULL) { PyGILState_STATE gilstate = PyGILState_Ensure(); id_release_weakref(id); @@ -2825,9 +2808,11 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } @@ -2955,9 +2940,11 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self, Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; + CLAMP_MIN(start, 0); } if (stop < 0) { stop += len; + CLAMP_MIN(stop, 0); } } @@ -4488,7 +4475,7 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr if ((ret == NULL) /* || BPy_PropDeferred_CheckTypeExact(ret) */ ) { StructRNA *srna = srna_from_self(cls, "StructRNA.__getattr__"); if (srna) { - PropertyRNA *prop = RNA_struct_type_find_property(srna, PyUnicode_AsUTF8(attr)); + PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, PyUnicode_AsUTF8(attr)); if (prop) { PointerRNA tptr; PyErr_Clear(); /* Clear error from tp_getattro. */ @@ -4510,7 +4497,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb const char *attr_str = PyUnicode_AsUTF8(attr); if (srna && !pyrna_write_check() && - (is_deferred_prop || RNA_struct_type_find_property(srna, attr_str))) { + (is_deferred_prop || RNA_struct_type_find_property_no_base(srna, attr_str))) { PyErr_Format(PyExc_AttributeError, "pyrna_struct_meta_idprop_setattro() " "can't set in readonly state '%.200s.%S'", @@ -7518,7 +7505,7 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna) /* Newclass will now have 2 ref's, ???, * probably 1 is internal since #Py_DECREF here segfaults. */ - /* PyC_ObSpit("new class ref", newclass); */ + // PyC_ObSpit("new class ref", newclass); if (newclass) { /* srna owns one, and the other is owned by the caller. */ @@ -7772,6 +7759,32 @@ void BPY_rna_init(void) return; } #endif + +#ifdef USE_PYRNA_INVALIDATE_WEAKREF + BLI_assert(id_weakref_pool == NULL); + id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool"); +#endif +} + +void BPY_rna_exit(void) +{ +#ifdef USE_PYRNA_INVALIDATE_WEAKREF + /* This can help track down which kinds of data were not released. + * If they were in fact freed by Blender, printing their names + * will crash giving a useful error with address sanitizer. The likely cause + * for this list not being empty is a missing call to: #BKE_libblock_free_data_py. */ + const int id_weakref_pool_len = BLI_ghash_len(id_weakref_pool); + if (id_weakref_pool_len != id_weakref_pool_len) { + printf("Found %d unreleased ID's\n", id_weakref_pool_len); + GHashIterator gh_iter; + GHASH_ITER (gh_iter, id_weakref_pool) { + ID *id = BLI_ghashIterator_getKey(&gh_iter); + printf("ID: %s\n", id->name); + } + } + BLI_ghash_free(id_weakref_pool, NULL, NULL); + id_weakref_pool = NULL; +#endif } /* 'bpy.data' from Python. */ @@ -8677,6 +8690,8 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param } #ifdef USE_PEDANTIC_WRITE + /* Handle nested draw calls, see: T89253. */ + const bool rna_disallow_writes_prev = rna_disallow_writes; rna_disallow_writes = is_readonly ? true : false; #endif /* *** Main Caller *** */ @@ -8686,7 +8701,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param /* *** Done Calling *** */ #ifdef USE_PEDANTIC_WRITE - rna_disallow_writes = false; + rna_disallow_writes = rna_disallow_writes_prev; #endif RNA_parameter_list_end(&iter); diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 24dbad53eb3..fd468bed470 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -181,6 +181,7 @@ StructRNA *srna_from_self(PyObject *self, const char *error_prefix); StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix); void BPY_rna_init(void); +void BPY_rna_exit(void); PyObject *BPY_rna_module(void); void BPY_update_rna_module(void); // PyObject *BPY_rna_doc(void); diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index abbc332d89d..fcc796d4545 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -334,7 +334,7 @@ static int validate_array_length(PyObject *rvalue, } if (tot != len) { - /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */ + // BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); PyErr_Format(PyExc_ValueError, "%s %.200s.%.200s, sequence must have %d items total, not %d", error_prefix, diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 1bb68babc3c..7bdb0e30410 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -227,7 +227,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * } if (!data_cb.is_subset && - /* We do not want to pre-add keys of flitered out types. */ + /* We do not want to pre-add keys of filtered out types. */ (key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) && /* We do not want to pre-add keys when we have filter on value types, * but not on key types. */ diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c index 490d9aa5212..d3ec54fc12d 100644 --- a/source/blender/python/intern/bpy_rna_operator.c +++ b/source/blender/python/intern/bpy_rna_operator.c @@ -91,7 +91,7 @@ static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data) } PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc, - ".. method:: poll_message_set(message, ...)\n" + ".. method:: poll_message_set(message, *args)\n" "\n" " Set the message to show in the tool-tip when poll fails.\n" "\n" diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index aa8cf8f2a9f..62f5a17c4dd 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -114,7 +114,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type, BLI_assert(py_struct_seq != NULL); for (str_iter = str_items; *str_iter; str_iter++) { - PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString((*str_iter))); + PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString(*str_iter)); } return py_struct_seq; diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 5beca7bd71a..be7dae6871b 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -95,7 +95,11 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len) x = 0x345678UL; i = 0; while (--len >= 0) { +#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */ + y = _Py_HashDouble(NULL, (double)(array[i++])); +#else y = _Py_HashDouble((double)(array[i++])); +#endif if (y == -1) { return -1; } diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index c2223b023ad..efcaa9b6a51 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -1926,7 +1926,7 @@ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2) return NULL; } -/* divid: obj / obj */ +/* divide: obj / obj */ static PyObject *Vector_div(PyObject *v1, PyObject *v2) { float *vec = NULL, scalar; @@ -2939,7 +2939,7 @@ static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS], memcpy(vec_cpy, vec->vec, vec_size * sizeof(float)); r_vec[3] = 1.0f; - /* muliplication */ + /* Multiplication. */ for (col = 0; col < mat->num_col; col++) { double dot = 0.0; for (row = 0; row < mat->num_row; row++) { diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index c73dea79aac..5868c76b28f 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -201,7 +201,7 @@ static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject } if (result == 0) { - /* collinear */ + /* Collinear. */ Py_RETURN_NONE; } diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index 6b2861bbefd..dfc0d5d0e9f 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -66,6 +66,7 @@ extern "C" { #define RE_USE_GPU_CONTEXT 512 #define RE_USE_CUSTOM_FREESTYLE 1024 #define RE_USE_NO_IMAGE_SAVE 2048 +#define RE_USE_ALEMBIC_PROCEDURAL 4096 /* RenderEngine.flag */ #define RE_ENGINE_ANIMATION 1 @@ -235,6 +236,12 @@ void RE_engines_register(RenderEngineType *render_type); bool RE_engine_is_opengl(RenderEngineType *render_type); +/** + * Return true if the RenderEngineType has native support for direct loading of Alembic data. For + * Cycles, this also checks that the experimental feature set is enabled. + */ +bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene); + RenderEngineType *RE_engines_find(const char *idname); rcti *RE_engine_get_current_tiles(struct Render *re, int *r_total_tiles, bool *r_needs_free); diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 657cd1f606b..75b3f2db249 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -128,6 +128,19 @@ bool RE_engine_is_opengl(RenderEngineType *render_type) return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); } +bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene) +{ + if ((render_type->flag & RE_USE_ALEMBIC_PROCEDURAL) == 0) { + return false; + } + + if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { + return false; + } + + return true; +} + /* Create, Free */ RenderEngine *RE_engine_create(RenderEngineType *type) diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 333ee9ecd33..479ad9209f0 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -2524,7 +2524,7 @@ void RE_RenderAnim(Render *re, if (G.is_break == true) { /* remove touched file */ if (is_movie == false && do_write_file) { - if ((rd.mode & R_TOUCH)) { + if (rd.mode & R_TOUCH) { if (!is_multiview_name) { if ((BLI_file_size(name) == 0)) { /* BLI_exists(name) is implicit */ diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 693cddbebbe..091f5964291 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -784,7 +784,7 @@ void render_result_views_new(RenderResult *rr, const RenderData *rd) render_result_views_free(rr); /* check renderdata for amount of views */ - if ((rd->scemode & R_MULTIVIEW)) { + if (rd->scemode & R_MULTIVIEW) { for (srv = rd->views.first; srv; srv = srv->next) { if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) { continue; diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index 31d5bf67f28..06dd570ce2c 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -494,10 +494,7 @@ static void free_pointdensity(PointDensity *pd) pd->point_tree = NULL; } - if (pd->point_data) { - MEM_freeN(pd->point_data); - pd->point_data = NULL; - } + MEM_SAFE_FREE(pd->point_data); pd->totpoints = 0; } diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c index 242c8a199fb..726124871ee 100644 --- a/source/blender/render/intern/zbuf.c +++ b/source/blender/render/intern/zbuf.c @@ -56,13 +56,8 @@ void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty) void zbuf_free_span(ZSpan *zspan) { if (zspan) { - if (zspan->span1) { - MEM_freeN(zspan->span1); - } - if (zspan->span2) { - MEM_freeN(zspan->span2); - } - zspan->span1 = zspan->span2 = NULL; + MEM_SAFE_FREE(zspan->span1); + MEM_SAFE_FREE(zspan->span2); } } diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 2941eb6f4c0..4025f1a4a04 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -79,14 +79,16 @@ struct Sequence *SEQ_add_image_strip(struct Main *bmain, struct Sequence *SEQ_add_sound_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data); + struct SeqLoadData *load_data, + const double audio_offset); struct Sequence *SEQ_add_meta_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, - struct SeqLoadData *load_data); + struct SeqLoadData *load_data, + double *r_video_start_offset); struct Sequence *SEQ_add_scene_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 7757271a2e5..3ca0555d9a5 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2164,11 +2164,7 @@ static int num_inputs_wipe(void) static void free_wipe_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_wipe_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2382,10 +2378,7 @@ static int num_inputs_transform(void) static void free_transform_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_transform_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2723,11 +2716,7 @@ static int num_inputs_glow(void) static void free_glow_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_glow_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -2853,11 +2842,7 @@ static int num_inputs_color(void) static void free_solid_color(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_solid_color(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -3111,10 +3096,7 @@ static void free_speed_effect(Sequence *seq, const bool UNUSED(do_id_user)) if (v->frameMap) { MEM_freeN(v->frameMap); } - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -3394,11 +3376,7 @@ static int num_inputs_gaussian_blur(void) static void free_gaussian_blur_effect(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static void copy_gaussian_blur_effect(Sequence *dst, Sequence *src, const int UNUSED(flag)) @@ -4052,11 +4030,7 @@ static void copy_effect_default(Sequence *dst, Sequence *src, const int UNUSED(f static void free_effect_default(Sequence *seq, const bool UNUSED(do_id_user)) { - if (seq->effectdata) { - MEM_freeN(seq->effectdata); - } - - seq->effectdata = NULL; + MEM_SAFE_FREE(seq->effectdata); } static int early_out_noop(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1)) diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 604c9900355..86bd840ce31 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -102,7 +102,7 @@ /* <cache type>-<resolution X>x<resolution Y>-<rendersize>%(<view_id>)-<frame no>.dcf */ #define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" #define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 1 +#define DCACHE_CURRENT_VERSION 2 #define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ typedef struct DiskCacheHeaderEntry { @@ -496,24 +496,34 @@ static size_t deflate_imbuf_to_file(ImBuf *ibuf, int level, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset, level); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + + /* Apply compression if wanted, otherwise just write directly to the file. */ + if (level > 0) { + return BLI_file_zstd_from_mem_at_pos( + data, header_entry->size_raw, file, header_entry->offset, level); } - return BLI_gzip_mem_to_file_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level); + fseek(file, header_entry->offset, SEEK_SET); + return fwrite(data, 1, header_entry->size_raw, file); } static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) { - if (ibuf->rect) { - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect, header_entry->size_raw, file, header_entry->offset); + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + char header[4]; + fseek(file, header_entry->offset, SEEK_SET); + if (fread(header, 1, sizeof(header), file) != sizeof(header)) { + return 0; + } + + /* Check if the data is compressed or raw. */ + if (BLI_file_magic_is_zstd(header)) { + return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); } - return BLI_ungzip_file_to_mem_at_pos( - ibuf->rect_float, header_entry->size_raw, file, header_entry->offset); + fseek(file, header_entry->offset, SEEK_SET); + return fread(data, 1, header_entry->size_raw, file); } static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index 1054dbeeba6..c53aacddcfe 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -111,7 +111,12 @@ void SEQ_sound_update_bounds(Scene *scene, Sequence *seq) /* We have to take into account start frame of the sequence's scene! */ int startofs = seq->startofs + seq->anim_startofs + seq->scene->r.sfra; - BKE_sound_move_scene_sound(scene, seq->scene_sound, seq->startdisp, seq->enddisp, startofs); + BKE_sound_move_scene_sound(scene, + seq->scene_sound, + seq->startdisp, + seq->enddisp, + startofs, + seq->sound->offset_time); } } else { diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index dab5593be37..9081c655d2f 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -99,7 +99,7 @@ void SEQ_add_load_data_init(SeqLoadData *load_data, static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq) { - SEQ_sequence_base_unique_name_recursive(scene, seqbase, seq); + SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); SEQ_time_update_sequence_bounds(scene, seq); SEQ_sort(seqbase); SEQ_relations_invalidate_cache_composite(scene, seq); @@ -382,9 +382,14 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL * \return created strip */ -Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) +Sequence *SEQ_add_sound_strip(Main *bmain, + Scene *scene, + ListBase *seqbase, + SeqLoadData *load_data, + const double audio_offset) { bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */ + sound->offset_time = audio_offset; SoundInfo info; bool sound_loaded = BKE_sound_info_get(bmain, sound, &info); @@ -398,14 +403,35 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL return NULL; } - Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM); + /* If this sound it part of a video, then the sound might start after the video. + * In this case we need to then offset the start frame of the audio so it syncs up + * properly with the video. + */ + int start_frame_offset = info.start_offset * FPS; + double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS; + + if (start_frame_offset_remainer > FLT_EPSILON) { + /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we + * start on a "whole" frame. + */ + start_frame_offset++; + } + + sound->offset_time += start_frame_offset_remainer; + + Sequence *seq = SEQ_sequence_alloc(seqbase, + load_data->start_frame + start_frame_offset, + load_data->channel, + SEQ_TYPE_SOUND_RAM); seq->sound = sound; seq->scene_sound = NULL; - /* We add a very small negative offset here, because - * ceil(132.0) == 133.0, not nice with videos, see T47135. */ - seq->len = MAX2(1, (int)ceil((double)info.length * FPS - 1e-4)); + /* We round the frame duration as the audio sample lengths usually does not + * line up with the video frames. Therefore we round this number to the + * nearest frame as the audio track usually overshoots or undershoots the + * end frame of the video by a little bit. + * See T47135 for under shoot example. */ + seq->len = MAX2(1, round((info.length - sound->offset_time) * FPS)); Strip *strip = seq->strip; /* We only need 1 element to store the filename. */ @@ -436,7 +462,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain), Scene *UNUSED(scene), ListBase *UNUSED(seqbase), - SeqLoadData *UNUSED(load_data)) + SeqLoadData *UNUSED(load_data), + const double UNUSED(audio_offset)) { return NULL; } @@ -477,7 +504,11 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ * \param load_data: SeqLoadData with information necessary to create strip * \return created strip */ -Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data) +Sequence *SEQ_add_movie_strip(Main *bmain, + Scene *scene, + ListBase *seqbase, + SeqLoadData *load_data, + double *r_video_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -552,6 +583,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); + *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]); IMB_anim_load_metadata(anim_arr[0]); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 68128690773..b73ac631693 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -34,6 +34,7 @@ #include "BKE_scene.h" #include "BKE_sound.h" +#include "DNA_sound_types.h" #include "IMB_imbuf.h" #include "SEQ_iterator.h" @@ -134,7 +135,8 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene, seq->scene_sound, seq->start + startofs, seq->start + seq->len - endofs, - startofs + seq->anim_startofs); + startofs + seq->anim_startofs, + seq->sound->offset_time); } } } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index e513c49c11b..4d65726fe2b 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -48,10 +48,6 @@ set(INC ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern ) -set(INC_SYS - ${ZLIB_INCLUDE_DIRS} -) - set(SRC intern/wm.c intern/wm_cursors.c @@ -203,7 +199,8 @@ if(WITH_XR_OPENXR) list(APPEND SRC xr/intern/wm_xr.c - xr/intern/wm_xr_actions.c + xr/intern/wm_xr_action.c + xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c xr/intern/wm_xr_session.c diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c0d408be2e0..3027df41e77 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -34,6 +34,7 @@ #include "BLI_sys_types.h" #include "DNA_windowmanager_types.h" #include "WM_keymap.h" +#include "WM_types.h" #ifdef __cplusplus extern "C" { @@ -73,8 +74,7 @@ struct wmNDOFMotionData; #endif #ifdef WITH_XR_OPENXR -struct wmXrActionState; -struct wmXrPose; +struct wmXrRuntimeData; #endif typedef struct wmGizmo wmGizmo; @@ -705,13 +705,13 @@ void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, i void WM_drag_free(struct wmDrag *drag); void WM_drag_data_free(int dragtype, void *poin); void WM_drag_free_list(struct ListBase *lb); - struct wmDropBox *WM_dropbox_add( ListBase *lb, const char *idname, - bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event, const char **), + bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event), void (*copy)(struct wmDrag *, struct wmDropBox *), - void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *)); + void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), + WMDropboxTooltipFunc tooltip); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ @@ -727,6 +727,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +const char *WM_drag_get_item_name(struct wmDrag *drag); + /* Set OpenGL viewport and scissor */ void wmViewport(const struct rcti *winrct); void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct); @@ -959,12 +961,18 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, float r_viewmat[4][4], float *r_focal_len); -bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr, unsigned int subaction_idx, float r_location[3]); -bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]); +bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]); /* wm_xr_actions.c */ /* XR action functions to be called pre-XR session start. @@ -977,41 +985,38 @@ bool WM_xr_action_create(wmXrData *xr, eXrActionType type, unsigned int count_subaction_paths, const char **subaction_paths, - const float *float_threshold, struct wmOperatorType *ot, struct IDProperty *op_properties, - eXrOpFlag op_flag); + const char *haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag); void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name); -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const struct wmXrPose *poses); -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths); bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses); void WM_xr_action_binding_destroy(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths); + const char *profile_path); /* If action_set_name is NULL, then all action sets will be treated as active. */ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name); -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name); +bool WM_xr_controller_pose_actions_set(wmXrData *xr, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name); /* XR action functions to be called post-XR session start. */ bool WM_xr_action_state_get(const wmXrData *xr, @@ -1022,10 +1027,48 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude); -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name); +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path); + +/* wm_xr_actionmap.c */ +XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime, + const char *name, + bool replace_existing); +void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src); +bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap); +XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name); +void WM_xr_actionmap_clear(XrActionMap *actionmap); +void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime); +ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime); +short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx); +short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime); +void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx); + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing); +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src); +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami); +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name); +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami); + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing); +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src); +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb); +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name); #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4ead0b2699c..843ceca7700 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -114,6 +114,8 @@ struct bContext; struct wmEvent; struct wmOperator; struct wmWindowManager; +struct wmDrag; +struct wmDropBox; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" @@ -326,11 +328,10 @@ typedef struct wmNotifier { #define ND_LAYOUTDELETE (2 << 16) #define ND_ANIMPLAY (4 << 16) #define ND_GPENCIL (5 << 16) -#define ND_EDITOR_CHANGED (6 << 16) /* Sent to new editors after switching to them. */ -#define ND_LAYOUTSET (7 << 16) -#define ND_SKETCH (8 << 16) -#define ND_WORKSPACE_SET (9 << 16) -#define ND_WORKSPACE_DELETE (10 << 16) +#define ND_LAYOUTSET (6 << 16) +#define ND_SKETCH (7 << 16) +#define ND_WORKSPACE_SET (8 << 16) +#define ND_WORKSPACE_DELETE (9 << 16) /* NC_SCENE Scene */ #define ND_SCENEBROWSE (1 << 16) @@ -934,6 +935,11 @@ typedef struct wmDragAsset { int import_type; /* eFileAssetImportType */ } wmDragAsset; +typedef char *(*WMDropboxTooltipFunc)(struct bContext *, + struct wmDrag *, + const struct wmEvent *event, + struct wmDropBox *drop); + typedef struct wmDrag { struct wmDrag *next, *prev; @@ -949,8 +955,8 @@ typedef struct wmDrag { float scale; int sx, sy; - /** If set, draws operator name. */ - char opname[200]; + /** If filled, draws operator tooltip/operator name. */ + char tooltip[200]; unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ @@ -964,8 +970,8 @@ typedef struct wmDrag { typedef struct wmDropBox { struct wmDropBox *next, *prev; - /** Test if the dropbox is active, then can print optype name. */ - bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *, const char **); + /** Test if the dropbox is active. */ + bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *); /** Before exec, this copies drag info to #wmDrop properties. */ void (*copy)(struct wmDrag *, struct wmDropBox *); @@ -976,6 +982,9 @@ typedef struct wmDropBox { */ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + /** Custom tooltip shown during dragging. */ + WMDropboxTooltipFunc tooltip; + /** * If poll succeeds, operator is called. * Not saved in file, so can be pointer. diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 5ec26a7b208..6f6a2402d89 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -616,11 +616,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int viewport[4] = {0, 0, region->winx, region->winy}; float co_3d_origin[3]; - /* Avoid multiple calculations. */ - struct GPUMatrixUnproject_Precalc unproj_precalc; - GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport); - - GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin); + GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); uint *buf_iter = buffer; int hit_found = -1; @@ -631,7 +627,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8]; float co_3d[3]; co_screen[2] = int_as_float(buf_iter[1]); - GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d); + GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d); float select_bias = gz->select_bias; if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) { select_bias *= gz->scale_final; diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 9657f8aa03c..e11ef52eb84 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -628,6 +628,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist) wm_close_and_free(C, wm); BLI_remlink(wmlist, wm); BKE_libblock_free_data(&wm->id, true); + BKE_libblock_free_data_py(&wm->id); MEM_freeN(wm); } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index db72dd2a819..76bb93b681c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -96,14 +96,16 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid) wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, - bool (*poll)(bContext *, wmDrag *, const wmEvent *, const char **), + bool (*poll)(bContext *, wmDrag *, const wmEvent *), void (*copy)(wmDrag *, wmDropBox *), - void (*cancel)(struct Main *, wmDrag *, wmDropBox *)) + void (*cancel)(struct Main *, wmDrag *, wmDropBox *), + WMDropboxTooltipFunc tooltip) { wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox"); drop->poll = poll; drop->copy = copy; drop->cancel = cancel; + drop->tooltip = tooltip; drop->ot = WM_operatortype_find(idname, 0); drop->opcontext = WM_OP_INVOKE_DEFAULT; @@ -218,22 +220,33 @@ void WM_drag_free_list(struct ListBase *lb) } } -static const char *dropbox_active(bContext *C, - ListBase *handlers, - wmDrag *drag, - const wmEvent *event) +static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) +{ + char *tooltip = NULL; + if (drop->tooltip) { + tooltip = drop->tooltip(C, drag, event, drop); + } + if (!tooltip) { + tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); + } + /* XXX Doing translation here might not be ideal, but later we have no more + * access to ot (and hence op context)... */ + return tooltip; +} + +static wmDropBox *dropbox_active(bContext *C, + ListBase *handlers, + wmDrag *drag, + const wmEvent *event) { 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) { - const char *tooltip = NULL; - if (drop->poll(C, drag, event, &tooltip) && + if (drop->poll(C, drag, event) && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { - /* XXX Doing translation here might not be ideal, but later we have no more - * access to ot (and hence op context)... */ - return (tooltip) ? tooltip : WM_operatortype_name(drop->ot, drop->ptr); + return drop; } } } @@ -242,29 +255,22 @@ static const char *dropbox_active(bContext *C, return NULL; } -/* return active operator name when mouse is in box */ -static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) +/* return active operator tooltip/name when mouse is in box */ +static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); - ScrArea *area = CTX_wm_area(C); - ARegion *region = CTX_wm_region(C); - const char *name; - - name = dropbox_active(C, &win->handlers, drag, event); - if (name) { - return name; + wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); + if (!drop) { + ScrArea *area = CTX_wm_area(C); + drop = dropbox_active(C, &area->handlers, drag, event); } - - name = dropbox_active(C, &area->handlers, drag, event); - if (name) { - return name; + if (!drop) { + ARegion *region = CTX_wm_region(C); + drop = dropbox_active(C, ®ion->handlers, drag, event); } - - name = dropbox_active(C, ®ion->handlers, drag, event); - if (name) { - return name; + if (drop) { + return dropbox_tooltip(C, drag, event, drop); } - return NULL; } @@ -279,17 +285,18 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e return; } - drag->opname[0] = 0; + drag->tooltip[0] = 0; /* check buttons (XXX todo rna and value) */ if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->opname, IFACE_("Paste name"), sizeof(drag->opname)); + BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); } else { - const char *opname = wm_dropbox_active(C, drag, event); + char *tooltip = wm_dropbox_active(C, drag, event); - if (opname) { - BLI_strncpy(drag->opname, opname, sizeof(drag->opname)); + if (tooltip) { + BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); + MEM_freeN(tooltip); // WM_cursor_modal_set(win, WM_CURSOR_COPY); } // else @@ -462,7 +469,7 @@ 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 const char *wm_drag_name(wmDrag *drag) +const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { case WM_DRAG_ID: { @@ -576,15 +583,15 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } if (rect) { - int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); + int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); drag_rect_minmax(rect, x, y, x + w, y + iconsize); } else { - UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), text_col); + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); } /* operator name with roundbox */ - if (drag->opname[0]) { + if (drag->tooltip[0]) { if (drag->imb) { x = cursorx - drag->sx / 2; @@ -607,11 +614,11 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) } if (rect) { - int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag)); + 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->opname, x, y); + wm_drop_operator_draw(drag->tooltip, x, y); } } } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index f01e28f8822..328950cf8f9 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -455,7 +455,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ * depth or multisample buffers. 3D view creates own buffers with * the data it needs. */ GPUOffScreen *offscreen = GPU_offscreen_create( - region->winx, region->winy, false, false, NULL); + region->winx, region->winy, false, GPU_RGBA8, NULL); if (!offscreen) { WM_report(RPT_ERROR, "Region could not be drawn!"); return; @@ -691,46 +691,48 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Then do actual drawing of regions. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->do_draw) { - CTX_wm_region_set(C, region); - bool use_viewport = WM_region_use_viewport(area, region); - - GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion"); - - if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) { - wm_draw_region_buffer_create(region, true, use_viewport); - - for (int view = 0; view < 2; view++) { - eStereoViews sview; - if (view == 0) { - sview = STEREO_LEFT_ID; - } - else { - sview = STEREO_RIGHT_ID; - wm_draw_region_stereo_set(bmain, area, region, sview); - } - - wm_draw_region_bind(region, view); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + if (!region->visible || !region->do_draw) { + continue; + } + + CTX_wm_region_set(C, region); + bool use_viewport = WM_region_use_viewport(area, region); + + GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion"); + + if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) { + wm_draw_region_buffer_create(region, true, use_viewport); + + for (int view = 0; view < 2; view++) { + eStereoViews sview; + if (view == 0) { + sview = STEREO_LEFT_ID; } - if (use_viewport) { - GPUViewport *viewport = region->draw_buffer->viewport; - GPU_viewport_stereo_composite(viewport, win->stereo3d_format); + else { + sview = STEREO_RIGHT_ID; + wm_draw_region_stereo_set(bmain, area, region, sview); } - } - else { - wm_draw_region_buffer_create(region, false, use_viewport); - wm_draw_region_bind(region, 0); + + wm_draw_region_bind(region, view); ED_region_do_draw(C, region); wm_draw_region_unbind(region); } + if (use_viewport) { + GPUViewport *viewport = region->draw_buffer->viewport; + GPU_viewport_stereo_composite(viewport, win->stereo3d_format); + } + } + else { + wm_draw_region_buffer_create(region, false, use_viewport); + wm_draw_region_bind(region, 0); + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); + } - GPU_debug_group_end(); + GPU_debug_group_end(); - region->do_draw = false; - CTX_wm_region_set(C, NULL); - } + region->do_draw = false; + CTX_wm_region_set(C, NULL); } CTX_wm_area_set(C, NULL); @@ -740,29 +742,30 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) /* Draw menus into their own framebuffer. */ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - if (region->visible) { - CTX_wm_menu_set(C, region); + if (!region->visible) { + continue; + } + CTX_wm_menu_set(C, region); - GPU_debug_group_begin("Menu"); + GPU_debug_group_begin("Menu"); - if (region->type && region->type->layout) { - /* UI code reads the OpenGL state, but we have to refresh - * the UI layout beforehand in case the menu size changes. */ - wmViewport(®ion->winrct); - region->type->layout(C, region); - } + if (region->type && region->type->layout) { + /* UI code reads the OpenGL state, but we have to refresh + * the UI layout beforehand in case the menu size changes. */ + wmViewport(®ion->winrct); + region->type->layout(C, region); + } - wm_draw_region_buffer_create(region, false, false); - wm_draw_region_bind(region, 0); - GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - ED_region_do_draw(C, region); - wm_draw_region_unbind(region); + wm_draw_region_buffer_create(region, false, false); + wm_draw_region_bind(region, 0); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + ED_region_do_draw(C, region); + wm_draw_region_unbind(region); - GPU_debug_group_end(); + GPU_debug_group_end(); - region->do_draw = false; - CTX_wm_menu_set(C, NULL); - } + region->do_draw = false; + CTX_wm_menu_set(C, NULL); } } @@ -786,8 +789,12 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blit non-overlapping area regions. */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->overlap == false) { - /* Blit from offscreen buffer. */ + if (!region->visible) { + continue; + } + + if (region->overlap == false) { + /* Blit from off-screen buffer. */ wm_draw_region_blit(region, view); } } @@ -796,24 +803,25 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Draw overlays and paint cursors. */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible) { - const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region); - const bool do_draw_overlay = (region->type && region->type->draw_overlay); - if (!(do_paint_cursor || do_draw_overlay)) { - continue; - } + if (!region->visible) { + continue; + } + const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region); + const bool do_draw_overlay = (region->type && region->type->draw_overlay); + if (!(do_paint_cursor || do_draw_overlay)) { + continue; + } - CTX_wm_area_set(C, area); - CTX_wm_region_set(C, region); - if (do_draw_overlay) { - wm_region_draw_overlay(C, area, region); - } - if (do_paint_cursor) { - wm_paintcursor_draw(C, area, region); - } - CTX_wm_region_set(C, NULL); - CTX_wm_area_set(C, NULL); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + if (do_draw_overlay) { + wm_region_draw_overlay(C, area, region); + } + if (do_paint_cursor) { + wm_paintcursor_draw(C, area, region); } + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); } } wmWindowViewport(win); @@ -821,7 +829,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blend in overlapping area regions */ ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->visible && region->overlap) { + if (!region->visible) { + continue; + } + if (region->overlap) { wm_draw_region_blend(region, 0, true); } } @@ -834,9 +845,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) /* Blend in floating regions (menus). */ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - if (region->visible) { - wm_draw_region_blend(region, 0, true); + if (!region->visible) { + continue; } + wm_draw_region_blend(region, 0, true); } /* always draw, not only when screen tagged */ @@ -888,7 +900,7 @@ static void wm_draw_window(bContext *C, wmWindow *win) * stereo methods, but it's less efficient than drawing directly. */ const int width = WM_window_pixels_x(win); const int height = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, false, NULL); + GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, GPU_RGBA8, NULL); if (offscreen) { GPUTexture *texture = GPU_offscreen_color_texture(offscreen); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 5e29a22304c..b9a3dd0c3fb 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2851,8 +2851,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = (ListBase *)event->customdata; LISTBASE_FOREACH (wmDrag *, drag, lb) { - const char *tooltip = NULL; - if (drop->poll(C, drag, event, &tooltip)) { + 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). */ @@ -3472,8 +3471,8 @@ void wm_event_do_handlers(bContext *C) } CTX_wm_area_set(C, NULL); - /* NOTE: do not escape on WM_HANDLER_BREAK, - * mousemove needs handled for previous area. */ + /* NOTE: do not escape on #WM_HANDLER_BREAK, + * mouse-move needs handled for previous area. */ } } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 06aaf95f232..f83511e76f0 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -28,11 +28,10 @@ * winsock stuff. */ #include <errno.h> +#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */ #include <stddef.h> #include <string.h> -#include "zlib.h" /* wm_read_exotic() */ - #ifdef WIN32 /* Need to include windows.h so _WIN32_IE is defined. */ # include <windows.h> @@ -51,6 +50,7 @@ #include "BLI_blenlib.h" #include "BLI_fileops_types.h" +#include "BLI_filereader.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_system.h" @@ -481,53 +481,64 @@ static void wm_init_userdef(Main *bmain) /* intended to check for non-blender formats but for now it only reads blends */ static int wm_read_exotic(const char *name) { - int len; - gzFile gzfile; + /* make sure we're not trying to read a directory.... */ + + int namelen = strlen(name); + if (namelen > 0 && ELEM(name[namelen - 1], '/', '\\')) { + return BKE_READ_EXOTIC_FAIL_PATH; + } + + /* open the file. */ + const int filedes = BLI_open(name, O_BINARY | O_RDONLY, 0); + if (filedes == -1) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + FileReader *rawfile = BLI_filereader_new_file(filedes); + if (rawfile == NULL) { + return BKE_READ_EXOTIC_FAIL_OPEN; + } + + /* read the header (7 bytes are enough to identify all known types). */ char header[7]; - int retval; + if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_FAIL_FORMAT; + } + rawfile->seek(rawfile, 0, SEEK_SET); - /* make sure we're not trying to read a directory.... */ + /* check for uncompressed .blend */ + if (STREQLEN(header, "BLENDER", 7)) { + rawfile->close(rawfile); + return BKE_READ_EXOTIC_OK_BLEND; + } - len = strlen(name); - if (len > 0 && ELEM(name[len - 1], '/', '\\')) { - retval = BKE_READ_EXOTIC_FAIL_PATH; + /* check for compressed .blend */ + FileReader *compressed_file = NULL; + if (BLI_file_magic_is_gzip(header)) { + /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd. + * While these files will no longer be written, there still needs to be reading support. */ + compressed_file = BLI_filereader_new_gzip(rawfile); + } + else if (BLI_file_magic_is_zstd(header)) { + compressed_file = BLI_filereader_new_zstd(rawfile); } - else { - gzfile = BLI_gzopen(name, "rb"); - if (gzfile == NULL) { - retval = BKE_READ_EXOTIC_FAIL_OPEN; - } - else { - len = gzread(gzfile, header, sizeof(header)); - gzclose(gzfile); - if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { - retval = BKE_READ_EXOTIC_OK_BLEND; - } - else { - /* We may want to support loading other file formats - * from their header bytes or file extension. - * This used to be supported in the code below and may be added - * back at some point. */ -#if 0 - WM_cursor_wait(true); - if (is_foo_format(name)) { - read_foo(name); - retval = BKE_READ_EXOTIC_OK_OTHER; - } - else -#endif - { - retval = BKE_READ_EXOTIC_FAIL_FORMAT; - } -#if 0 - WM_cursor_wait(false); -#endif - } + /* If a compression signature matches, try decompressing the start and check if it's a .blend */ + if (compressed_file != NULL) { + size_t len = compressed_file->read(compressed_file, header, sizeof(header)); + compressed_file->close(compressed_file); + if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) { + return BKE_READ_EXOTIC_OK_BLEND; } } + else { + rawfile->close(rawfile); + } - return retval; + /* Add check for future file formats here. */ + + return BKE_READ_EXOTIC_FAIL_FORMAT; } /** \} */ @@ -596,19 +607,33 @@ static void wm_file_read_pre(bContext *C, bool use_data, bool UNUSED(use_userdef } /** + * Parameters for #wm_file_read_post, also used for deferred initialization. + */ +struct wmFileReadPost_Params { + uint use_data : 1; + uint use_userdef : 1; + + uint is_startup_file : 1; + uint is_factory_startup : 1; + uint reset_app_template : 1; +}; + +/** * Logic shared between #WM_file_read & #wm_homefile_read, * updates to make after reading a file. */ -static void wm_file_read_post(bContext *C, - const bool is_startup_file, - const bool is_factory_startup, - const bool use_data, - const bool use_userdef, - const bool reset_app_template) +static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *params) { - bool addons_loaded = false; wmWindowManager *wm = CTX_wm_manager(C); + const bool use_data = params->use_data; + const bool use_userdef = params->use_userdef; + const bool is_startup_file = params->is_startup_file; + const bool is_factory_startup = params->is_factory_startup; + const bool reset_app_template = params->reset_app_template; + + bool addons_loaded = false; + if (use_data) { if (!G.background) { /* remove windows which failed to be added via WM_check */ @@ -797,22 +822,31 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) bf_reports->count.resynced_lib_overrides, duration_lib_override_recursive_resync_minutes, duration_lib_override_recursive_resync_seconds); + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != NULL; node_lib = node_lib->next) { Library *library = node_lib->link; BKE_reportf( - bf_reports->reports, RPT_INFO, "Library %s needs overrides resync.", library->filepath); + bf_reports->reports, RPT_INFO, "Library %s needs overrides resync", library->filepath); } } + if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) { BKE_reportf(bf_reports->reports, RPT_WARNING, - "%d libraries and %d linked data-blocks are missing, please check the " - "Info and Outliner editors for details", + "%d libraries and %d linked data-blocks are missing (including %d ObjectData and " + "%d Proxies), please check the Info and Outliner editors for details", bf_reports->count.missing_libraries, - bf_reports->count.missing_linked_id); + bf_reports->count.missing_linked_id, + bf_reports->count.missing_obdata, + bf_reports->count.missing_obproxies); } + else { + BLI_assert(bf_reports->count.missing_obdata == 0); + BLI_assert(bf_reports->count.missing_obproxies == 0); + } + if (bf_reports->resynced_lib_overrides_libraries_count != 0) { BKE_reportf(bf_reports->reports, RPT_WARNING, @@ -901,7 +935,14 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) wm_history_file_update(); } - wm_file_read_post(C, false, false, use_data, use_userdef, false); + wm_file_read_post(C, + &(const struct wmFileReadPost_Params){ + .use_data = use_data, + .use_userdef = use_userdef, + .is_startup_file = false, + .is_factory_startup = false, + .reset_app_template = false, + }); bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole; file_read_reports_finalize(&bf_reports); @@ -984,31 +1025,18 @@ const char *WM_init_state_app_template_get(void) /** * Called on startup, (context entirely filled with NULLs) - * or called for 'New File' both startup.blend and userpref.blend are checked. - * - * \param use_factory_settings: - * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead. - * Used for "Restore Factory Settings". + * or called for 'New File' both `startup.blend` and `userpref.blend` are checked. * - * \param use_userdef: Load factory settings as well as startup file. - * Disabled for "File New" we don't want to reload preferences. - * - * \param filepath_startup_override: - * Optional path pointing to an alternative blend file (may be NULL). - * - * \param app_template_override: - * Template to use instead of the template defined in user-preferences. - * When not-null, this is written into the user preferences. + * \param r_params_file_read_post: Support postponed initialization, + * needed for initial startup when only some sub-systems have been initialized. + * When non-null, #wm_file_read_post doesn't run, instead it's arguments are stored + * in this return argument. + * The caller is responsible for calling #wm_homefile_read_post with this return argument. */ -void wm_homefile_read(bContext *C, - ReportList *reports, - bool use_factory_settings, - bool use_empty_data, - bool use_data, - bool use_userdef, - const char *filepath_startup_override, - const char *app_template_override, - bool *r_is_factory_startup) +void wm_homefile_read_ex(bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + ReportList *reports, + struct wmFileReadPost_Params **r_params_file_read_post) { #if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */ /* Context does not always have valid main pointer here. */ @@ -1017,6 +1045,14 @@ void wm_homefile_read(bContext *C, ListBase wmbase; bool success = false; + /* May be enabled, when the user configuration doesn't exist. */ + const bool use_data = params_homefile->use_data; + const bool use_userdef = params_homefile->use_userdef; + bool use_factory_settings = params_homefile->use_factory_settings; + const bool use_empty_data = params_homefile->use_empty_data; + const char *filepath_startup_override = params_homefile->filepath_startup_override; + const char *app_template_override = params_homefile->app_template_override; + bool filepath_startup_is_factory = true; char filepath_startup[FILE_MAX]; char filepath_userdef[FILE_MAX]; @@ -1304,13 +1340,45 @@ void wm_homefile_read(bContext *C, G.save_over = 0; } - wm_file_read_post(C, true, is_factory_startup, use_data, use_userdef, reset_app_template); + { + const struct wmFileReadPost_Params params_file_read_post = { + .use_data = use_data, + .use_userdef = use_userdef, + .is_startup_file = true, + .is_factory_startup = is_factory_startup, + .reset_app_template = reset_app_template, + }; + if (r_params_file_read_post == NULL) { + wm_file_read_post(C, ¶ms_file_read_post); + } + else { + *r_params_file_read_post = MEM_mallocN(sizeof(struct wmFileReadPost_Params), __func__); + **r_params_file_read_post = params_file_read_post; - if (r_is_factory_startup) { - *r_is_factory_startup = is_factory_startup; + /* Match #wm_file_read_post which leaves the window cleared too. */ + CTX_wm_window_set(C, NULL); + } } } +void wm_homefile_read(bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + ReportList *reports) +{ + wm_homefile_read_ex(C, params_homefile, reports, NULL); +} + +/** + * Special case, support deferred execution of #wm_file_read_post, + * Needed when loading for the first time to workaround order of initialization bug, see T89046. + */ +void wm_homefile_read_post(struct bContext *C, + const struct wmFileReadPost_Params *params_file_read_post) +{ + wm_file_read_post(C, params_file_read_post); + MEM_freeN((void *)params_file_read_post); +} + /* -------------------------------------------------------------------- */ /** \name Blend-File History API * \{ */ @@ -2078,14 +2146,15 @@ static int wm_userpref_read_exec(bContext *C, wmOperator *op) UserDef U_backup = U; wm_homefile_read(C, - op->reports, - use_factory_settings, - false, - use_data, - use_userdef, - NULL, - WM_init_state_app_template_get(), - NULL); + &(const struct wmHomeFileRead_Params){ + .use_data = use_data, + .use_userdef = use_userdef, + .use_factory_settings = use_factory_settings, + .use_empty_data = false, + .filepath_startup_override = NULL, + .app_template_override = WM_init_state_app_template_get(), + }, + op->reports); wm_userpref_read_exceptions(&U, &U_backup); SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT); @@ -2224,16 +2293,17 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) app_template = WM_init_state_app_template_get(); } - bool use_data = true; wm_homefile_read(C, - op->reports, - use_factory_settings, - use_empty_data, - use_data, - use_userdef, - filepath, - app_template, - NULL); + &(const struct wmHomeFileRead_Params){ + .use_data = true, + .use_userdef = use_userdef, + .use_factory_settings = use_factory_settings, + .use_empty_data = use_empty_data, + .filepath_startup_override = filepath, + .app_template_override = app_template, + }, + op->reports); + if (use_splash) { WM_init_splash(C); } @@ -3434,7 +3504,7 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, BLI_split_file_part(blendfile_pathpath, filename, sizeof(filename)); } else { - STRNCPY(filename, IFACE_("untitled.blend")); + STRNCPY(filename, "untitled.blend"); } uiItemL(layout, filename, ICON_NONE); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index fec5a516688..606c9252ff9 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -30,6 +30,8 @@ #include <stdio.h> #include <string.h> +#include "CLG_log.h" + #include "MEM_guardedalloc.h" #include "DNA_ID.h" @@ -76,6 +78,8 @@ #include "wm_files.h" +static CLG_LogRef LOG = {"wm.files_link"}; + /* -------------------------------------------------------------------- */ /** \name Link/Append Operator * \{ */ @@ -315,7 +319,7 @@ static bool wm_link_append_item_poll(ReportList *reports, short idcode; if (!group || !name) { - printf("skipping %s\n", path); + CLOG_WARN(&LOG, "Skipping %s", path); return false; } @@ -485,11 +489,11 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) } /* XXX We'd need re-entrant locking on Main for this to work... */ - /* BKE_main_lock(bmain); */ + // BKE_main_lock(bmain); wm_link_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); - /* BKE_main_unlock(bmain); */ + // BKE_main_unlock(bmain); /* mark all library linked objects to be updated */ BKE_main_lib_objects_recalc_all(bmain); @@ -759,12 +763,12 @@ static void lib_relocate_do_remap(Main *bmain, BLI_assert(new_id); } if (new_id) { -#ifdef PRINT_DEBUG - printf("before remap of %s, old_id users: %d, new_id users: %d\n", - old_id->name, - old_id->us, - new_id->us); -#endif + CLOG_INFO(&LOG, + 4, + "Before remap of %s, old_id users: %d, new_id users: %d", + old_id->name, + old_id->us, + new_id->us); BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags); if (old_id->flag & LIB_FAKEUSER) { @@ -772,12 +776,12 @@ static void lib_relocate_do_remap(Main *bmain, id_fake_user_set(new_id); } -#ifdef PRINT_DEBUG - printf("after remap of %s, old_id users: %d, new_id users: %d\n", - old_id->name, - old_id->us, - new_id->us); -#endif + CLOG_INFO(&LOG, + 4, + "After remap of %s, old_id users: %d, new_id users: %d", + old_id->name, + old_id->us, + new_id->us); /* In some cases, new_id might become direct link, remove parent of library in this case. */ if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) { @@ -831,7 +835,7 @@ static void lib_relocate_do_remap(Main *bmain, } } -static void lib_relocate_do(Main *bmain, +static void lib_relocate_do(bContext *C, Library *library, WMLinkAppendData *lapp_data, ReportList *reports, @@ -843,6 +847,10 @@ static void lib_relocate_do(Main *bmain, LinkNode *itemlink; int item_idx; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + /* Remove all IDs to be reloaded from Main. */ lba_idx = set_listbasepointers(bmain, lbarray); while (lba_idx--) { @@ -871,9 +879,7 @@ static void lib_relocate_do(Main *bmain, item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); BLI_bitmap_set_all(item->libraries, true, lapp_data->num_libraries); -#ifdef PRINT_DEBUG - printf("\tdatablock to seek for: %s\n", id->name); -#endif + CLOG_INFO(&LOG, 4, "Datablock to seek for: %s", id->name); } } } @@ -990,21 +996,31 @@ static void lib_relocate_do(Main *bmain, } } - /* Update overrides of reloaded linked data-blocks. - * Note that this will not necessarily fully update the override, it might need to be manually - * 're-generated' depending on changes in linked data. */ + /* Update overrides of reloaded linked data-blocks. */ ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id) || (id->tag & LIB_TAG_PRE_EXISTING) == 0) { continue; } - if (id->override_library->reference->lib == library) { + if ((id->override_library->reference->tag & LIB_TAG_PRE_EXISTING) == 0) { BKE_lib_override_library_update(bmain, id); } } FOREACH_MAIN_ID_END; + /* Resync overrides if needed. */ + if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) { + BKE_lib_override_library_main_resync(bmain, + scene, + view_layer, + &(struct BlendFileReadReport){ + .reports = reports, + }); + /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ + BKE_lib_override_library_main_operations_create(bmain, true); + } + BKE_main_collection_sync(bmain); BKE_main_lib_objects_recalc_all(bmain); @@ -1039,7 +1055,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) wm_link_append_data_library_add(lapp_data, lib->filepath_abs); - lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true); + lib_relocate_do(C, lib, lapp_data, reports, true); wm_link_append_data_free(lapp_data); @@ -1103,9 +1119,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) } if (BLI_path_cmp(lib->filepath_abs, path) == 0) { -#ifdef PRINT_DEBUG - printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us); -#endif + CLOG_INFO(&LOG, 4, "We are supposed to reload '%s' lib (%d)", lib->filepath, lib->id.us); do_reload = true; @@ -1115,9 +1129,8 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) else { int totfiles = 0; -#ifdef PRINT_DEBUG - printf("We are supposed to relocate '%s' lib to new '%s' one...\n", lib->filepath, libname); -#endif + CLOG_INFO( + &LOG, 4, "We are supposed to relocate '%s' lib to new '%s' one", lib->filepath, libname); /* Check if something is indicated for relocate. */ prop = RNA_struct_find_property(op->ptr, "files"); @@ -1143,17 +1156,13 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) continue; } -#ifdef PRINT_DEBUG - printf("\t candidate new lib to reload datablocks from: %s\n", path); -#endif + CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); wm_link_append_data_library_add(lapp_data, path); } RNA_END; } else { -#ifdef PRINT_DEBUG - printf("\t candidate new lib to reload datablocks from: %s\n", path); -#endif + CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); wm_link_append_data_library_add(lapp_data, path); } } @@ -1162,7 +1171,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT; } - lib_relocate_do(bmain, lib, lapp_data, op->reports, do_reload); + lib_relocate_do(C, lib, lapp_data, op->reports, do_reload); wm_link_append_data_free(lapp_data); diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 92ca0b87527..1c736647084 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -585,7 +585,7 @@ void wm_tweakevent_test(bContext *C, const wmEvent *event, int action) } else { /* no tweaks if event was handled */ - if ((action & WM_HANDLER_BREAK)) { + if (action & WM_HANDLER_BREAK) { WM_gesture_end(win, win->tweak); } else { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index d7ea47fc625..a8d2e000108 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -222,7 +222,10 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time) } } -/* only called once, for startup */ +/** + * Initialize Blender and load the startup file & preferences + * (only called once). + */ void WM_init(bContext *C, int argc, const char **argv) { @@ -248,24 +251,22 @@ void WM_init(bContext *C, int argc, const char **argv) ED_undosys_type_init(); - BKE_library_callback_free_notifier_reference_set( - WM_main_remove_notifier_reference); /* lib_id.c */ - BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); /* screen.c */ + BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); + BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh); - BKE_library_callback_remap_editor_id_reference_set( - WM_main_remap_editor_id_reference); /* lib_id.c */ - BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ + BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); + BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update); - ED_spacetypes_init(); /* editors/space_api/spacetype.c */ + ED_spacetypes_init(); ED_node_init_butfuncs(); BLF_init(); BLT_lang_init(); - /* Must call first before doing any '.blend' file reading, - * since versioning code may create new IDs... See T57066. */ + /* Must call first before doing any `.blend` file reading, + * since versioning code may create new IDs. See T57066. */ BLT_lang_set(NULL); /* Init icons before reading .blend files for preview icons, which can @@ -273,37 +274,57 @@ void WM_init(bContext *C, int argc, const char **argv) * for scripts that do background processing with preview icons. */ BKE_icons_init(BIFICONID_LAST); - /* reports can't be initialized before the wm, + /* Reports can't be initialized before the window-manager, * but keep before file reading, since that may report errors */ wm_init_reports(C); WM_msgbus_types_init(); - /* get the default database, plus a wm */ - bool is_factory_startup = true; - const bool use_data = true; - const bool use_userdef = true; - /* Studio-lights needs to be init before we read the home-file, * otherwise the versioning cannot find the default studio-light. */ BKE_studiolight_init(); BLI_assert((G.fileflags & G_FILE_NO_UI) == 0); - wm_homefile_read(C, - NULL, - G.factory_startup, - false, - use_data, - use_userdef, - NULL, - WM_init_state_app_template_get(), - &is_factory_startup); - - /* Call again to set from userpreferences... */ + /** + * NOTE(@campbellbarton): Startup file and order of initialization. + * + * Loading #BLENDER_STARTUP_FILE, #BLENDER_USERPREF_FILE, starting Python and other sub-systems, + * have inter-dependencies, for example. + * + * - Some sub-systems depend on the preferences (initializing icons depend on the theme). + * - Add-ons depends on the preferences to know what has been enabled. + * - Add-ons depends on the window-manger to register their key-maps. + * - Evaluating the startup file depends on Python for animation-drivers (see T89046). + * - Starting Python depends on the startup file so key-maps can be added in the window-manger. + * + * Loading preferences early, then application subsystems and finally the startup data would + * simplify things if it weren't for key-maps being part of the window-manager + * which is blend file data. + * Creating a dummy window-manager early, or moving the key-maps into the preferences + * would resolve this and may be worth looking into long-term, see: D12184 for details. + */ + struct wmFileReadPost_Params *params_file_read_post = NULL; + wm_homefile_read_ex(C, + &(const struct wmHomeFileRead_Params){ + .use_data = true, + .use_userdef = true, + .use_factory_settings = G.factory_startup, + .use_empty_data = false, + .filepath_startup_override = NULL, + .app_template_override = WM_init_state_app_template_get(), + }, + NULL, + ¶ms_file_read_post); + + /* NOTE: leave `G_MAIN->name` set to an empty string since this + * matches behavior after loading a new file. */ + BLI_assert(G_MAIN->name[0] == '\0'); + + /* Call again to set from preferences. */ BLT_lang_set(NULL); - /* For fsMenu. Called here so can include user preference paths if needed. */ + /* For file-system. Called here so can include user preference paths if needed. */ ED_file_init(); /* That one is generated on demand, we need to be sure it's clear on init. */ @@ -312,12 +333,13 @@ void WM_init(bContext *C, int argc, const char **argv) if (!G.background) { #ifdef WITH_INPUT_NDOF - /* sets 3D mouse deadzone */ + /* Sets 3D mouse dead-zone. */ WM_ndof_deadzone_set(U.ndof_deadzone); #endif WM_init_opengl(); if (!WM_platform_support_perform_checks()) { + /* No attempt to avoid memory leaks here. */ exit(-1); } @@ -328,20 +350,11 @@ void WM_init(bContext *C, int argc, const char **argv) ED_spacemacros_init(); - /* NOTE(campbell): there is a bug where python needs initializing before loading the - * startup.blend because it may contain PyDrivers. It also needs to be after - * initializing space types and other internal data. - * - * However can't redo this at the moment. Solution is to load python - * before wm_homefile_read() or make py-drivers check if python is running. - * Will try fix when the crash can be repeated. */ - #ifdef WITH_PYTHON BPY_python_start(C, argc, argv); BPY_python_reset(C); #else - (void)argc; /* unused */ - (void)argv; /* unused */ + UNUSED_VARS(argc, argv); #endif if (!G.background) { @@ -358,14 +371,6 @@ void WM_init(bContext *C, int argc, const char **argv) wm_history_file_read(); - /* allow a path of "", this is what happens when making a new file */ -#if 0 - if (BKE_main_blendfile_path_from_global()[0] == '\0') { - BLI_join_dirfile( - G_MAIN->name, sizeof(G_MAIN->name), BKE_appdir_folder_default(), "untitled.blend"); - } -#endif - BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib)); #ifdef WITH_COMPOSITOR @@ -375,30 +380,7 @@ void WM_init(bContext *C, int argc, const char **argv) } #endif - { - Main *bmain = CTX_data_main(C); - /* NOTE: logic here is from wm_file_read_post, - * call functions that depend on Python being initialized. */ - - /* normally 'wm_homefile_read' will do this, - * however python is not initialized when called from this function. - * - * unlikely any handlers are set but its possible, - * note that recovering the last session does its own callbacks. */ - CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first); - - BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE); - BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST); - if (is_factory_startup) { - BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); - } - - wm_file_read_report(C, bmain); - - if (!G.background) { - CTX_wm_window_set(C, NULL); - } - } + wm_homefile_read_post(C, params_file_read_post); } void WM_init_splash(bContext *C) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 8a8ea18cc43..b5a038757c2 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2766,10 +2766,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); ScrArea *area = CTX_wm_area(C); - if (rc->dial) { - MEM_freeN(rc->dial); - rc->dial = NULL; - } + MEM_SAFE_FREE(rc->dial); ED_area_status_text(area, NULL); @@ -2959,10 +2956,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (event->val == KM_RELEASE) { rc->slow_mode = false; handled = true; - if (rc->dial) { - MEM_freeN(rc->dial); - rc->dial = NULL; - } + MEM_SAFE_FREE(rc->dial); } break; } @@ -3184,10 +3178,11 @@ static void redraw_timer_step(bContext *C, LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { CTX_wm_area_set(C, area_iter); LISTBASE_FOREACH (ARegion *, region_iter, &area_iter->regionbase) { - if (region_iter->visible) { - CTX_wm_region_set(C, region_iter); - wm_draw_region_test(C, area_iter, region_iter); + if (!region_iter->visible) { + continue; } + CTX_wm_region_set(C, region_iter); + wm_draw_region_test(C, area_iter, region_iter); } } diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 1b08b8dad92..93417213a65 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1058,7 +1058,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); if (win != wm->windrawable && win->ghostwin) { - // win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */ + // win->lmbut = 0; /* Keeps hanging when mouse-pressed while other window opened. */ wm_window_clear_drawable(wm); if (G.debug & G_DEBUG_EVENTS) { @@ -1416,7 +1416,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_add(win, &event); - /* printf("Drop detected\n"); */ + // printf("Drop detected\n"); /* add drag data to wm for paths: */ diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index c7fe07cad7f..2fa5a68829e 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -24,6 +24,7 @@ #pragma once struct Main; +struct wmFileReadPost_Params; struct wmGenericCallback; struct wmOperatorType; @@ -33,15 +34,45 @@ extern "C" { /* wm_files.c */ void wm_history_file_read(void); + +struct wmHomeFileRead_Params { + /** Load data, disable when only loading user preferences. */ + unsigned int use_data : 1; + /** Load factory settings as well as startup file (disabled for "File New"). */ + unsigned int use_userdef : 1; + + /** + * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead. + * Used for "Restore Factory Settings". + */ + unsigned int use_factory_settings : 1; + /** + * Load the startup file without any data-blocks. + * Useful for automated content generation, so the file starts without data. + */ + unsigned int use_empty_data : 1; + /** + * Optional path pointing to an alternative blend file (may be NULL). + */ + const char *filepath_startup_override; + /** + * Template to use instead of the template defined in user-preferences. + * When not-null, this is written into the user preferences. + */ + const char *app_template_override; +}; + +void wm_homefile_read_ex(struct bContext *C, + const struct wmHomeFileRead_Params *params_homefile, + struct ReportList *reports, + struct wmFileReadPost_Params **r_params_file_read_post); void wm_homefile_read(struct bContext *C, - struct ReportList *reports, - bool use_factory_settings, - bool use_empty_data, - bool use_data, - bool use_userdef, - const char *filepath_startup_override, - const char *app_template_override, - bool *r_is_factory_startup); + const struct wmHomeFileRead_Params *params_homefile, + struct ReportList *reports); + +void wm_homefile_read_post(struct bContext *C, + const struct wmFileReadPost_Params *params_file_read_post); + void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 2a67c2bee9f..3091a3a19f1 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -115,6 +115,7 @@ bool wm_xr_init(wmWindowManager *wm) void wm_xr_exit(wmWindowManager *wm) { if (wm->xr.runtime != NULL) { + WM_xr_actionmaps_clear(wm->xr.runtime); wm_xr_runtime_data_free(&wm->xr.runtime); } if (wm->xr.session_settings.shading.prop) { @@ -148,6 +149,7 @@ bool wm_xr_events_handle(wmWindowManager *wm) wmXrRuntimeData *wm_xr_runtime_data_create(void) { wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__); + runtime->actactionmap = runtime->selactionmap = -1; return runtime; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index 7eabd29baa0..ba347c537ec 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actions.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -23,6 +23,7 @@ * All functions are designed to be usable by RNA / the Python API. */ +#include "BLI_listbase.h" #include "BLI_math.h" #include "GHOST_C-api.h" @@ -56,6 +57,9 @@ static void action_set_destroy(void *val) MEM_SAFE_FREE(action_set->name); + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + MEM_freeN(action_set); } @@ -68,10 +72,15 @@ static wmXrAction *action_create(const char *action_name, eXrActionType type, unsigned int count_subaction_paths, const char **subaction_paths, - const float *float_threshold, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char *haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag) { wmXrAction *action = MEM_callocN(sizeof(*action), __func__); action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name"); @@ -109,15 +118,32 @@ static wmXrAction *action_create(const char *action_name, action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); - if (float_threshold) { - BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); - action->float_threshold = *float_threshold; - CLAMP(action->float_threshold, 0.0f, 1.0f); + const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); + if (is_float_action) { + action->float_thresholds = MEM_calloc_arrayN( + count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"); + } + if (is_button_action) { + action->axis_flags = MEM_calloc_arrayN( + count, sizeof(*action->axis_flags), "XrAction_AxisFlags"); } action->ot = ot; action->op_properties = op_properties; + + if (haptic_name) { + BLI_assert(is_button_action); + action->haptic_name = MEM_mallocN(strlen(haptic_name) + 1, "XrAction_HapticName"); + strcpy(action->haptic_name, haptic_name); + action->haptic_duration = *haptic_duration; + action->haptic_frequency = *haptic_frequency; + action->haptic_amplitude = *haptic_amplitude; + } + action->op_flag = op_flag; + action->action_flag = action_flag; + action->haptic_flag = haptic_flag; return action; } @@ -140,6 +166,11 @@ static void action_destroy(void *val) MEM_SAFE_FREE(action->states); MEM_SAFE_FREE(action->states_prev); + MEM_SAFE_FREE(action->float_thresholds); + MEM_SAFE_FREE(action->axis_flags); + + MEM_SAFE_FREE(action->haptic_name); + MEM_freeN(action); } @@ -179,13 +210,14 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name) wmXrSessionState *session_state = &xr->runtime->session_state; if (action_set == session_state->active_action_set) { - if (action_set->controller_pose_action) { + if (action_set->controller_grip_action || action_set->controller_aim_action) { wm_xr_session_controller_data_clear(session_state); - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action) { - action_set->active_modal_action = NULL; + action_set->controller_grip_action = action_set->controller_aim_action = NULL; } + + BLI_freelistN(&action_set->active_modal_actions); + BLI_freelistN(&action_set->active_haptic_actions); + session_state->active_action_set = NULL; } @@ -198,10 +230,15 @@ bool WM_xr_action_create(wmXrData *xr, eXrActionType type, unsigned int count_subaction_paths, const char **subaction_paths, - const float *float_threshold, wmOperatorType *ot, IDProperty *op_properties, - eXrOpFlag op_flag) + const char *haptic_name, + const int64_t *haptic_duration, + const float *haptic_frequency, + const float *haptic_amplitude, + eXrOpFlag op_flag, + eXrActionFlag action_flag, + eXrHapticFlag haptic_flag) { if (action_find(xr, action_set_name, action_name)) { return false; @@ -211,16 +248,23 @@ bool WM_xr_action_create(wmXrData *xr, type, count_subaction_paths, subaction_paths, - float_threshold, ot, op_properties, - op_flag); + haptic_name, + haptic_duration, + haptic_frequency, + haptic_amplitude, + op_flag, + action_flag, + haptic_flag); GHOST_XrActionInfo info = { .name = action_name, .count_subaction_paths = count_subaction_paths, .subaction_paths = subaction_paths, .states = action->states, + .float_thresholds = action->float_thresholds, + .axis_flags = (int16_t *)action->axis_flags, .customdata_free_fn = action_destroy, .customdata = action, }; @@ -257,110 +301,88 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char return; } - if (action_set->controller_pose_action && - STREQ(action_set->controller_pose_action->name, action_name)) { - if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_clear(&xr->runtime->session_state); - } - action_set->controller_pose_action = NULL; - } - if (action_set->active_modal_action && - STREQ(action_set->active_modal_action->name, action_name)) { - action_set->active_modal_action = NULL; - } - wmXrAction *action = action_find(xr, action_set_name, action_name); if (!action) { return; } -} - -bool WM_xr_action_space_create(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths, - const wmXrPose *poses) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; - GHOST_XrPose *ghost_poses = MEM_malloc_arrayN( - count_subaction_paths, sizeof(*ghost_poses), __func__); - for (unsigned int i = 0; i < count_subaction_paths; ++i) { - const wmXrPose *pose = &poses[i]; - GHOST_XrPose *ghost_pose = &ghost_poses[i]; - copy_v3_v3(ghost_pose->position, pose->position); - copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat); + if ((action_set->controller_grip_action && + STREQ(action_set->controller_grip_action->name, action_name)) || + (action_set->controller_aim_action && + STREQ(action_set->controller_aim_action->name, action_name))) { + if (action_set == xr->runtime->session_state.active_action_set) { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); + } + action_set->controller_grip_action = action_set->controller_aim_action = NULL; } - info.poses = ghost_poses; - bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true : - false; - MEM_freeN(ghost_poses); - return ret; -} + LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) { + wmXrAction *active_modal_action = ld->data; + if (STREQ(active_modal_action->name, action_name)) { + BLI_freelinkN(&action_set->active_modal_actions, ld); + break; + } + } -void WM_xr_action_space_destroy(wmXrData *xr, - const char *action_set_name, - const char *action_name, - unsigned int count_subaction_paths, - const char **subaction_paths) -{ - GHOST_XrActionSpaceInfo info = { - .action_name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, - }; + LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, &action_set->active_haptic_actions) { + if (STREQ(ha->action->name, action_name)) { + BLI_freelinkN(&action_set->active_haptic_actions, ha); + } + } - GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info); + GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name); } bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) + const char *profile_path, + unsigned int count_subaction_paths, + const char **subaction_paths, + const char **component_paths, + const float *float_thresholds, + const eXrAxisFlag *axis_flags, + const struct wmXrPose *poses) { - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; + GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( + count_subaction_paths, sizeof(*binding_infos), __func__); + + for (unsigned int i = 0; i < count_subaction_paths; ++i) { + GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; + binding_info->component_path = component_paths[i]; + if (float_thresholds) { + binding_info->float_threshold = float_thresholds[i]; + } + if (axis_flags) { + binding_info->axis_flag = axis_flags[i]; + } + if (poses) { + copy_v3_v3(binding_info->pose.position, poses[i].position); + copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat); + } + } GHOST_XrActionProfileInfo profile_info = { + .action_name = action_name, .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, + .count_subaction_paths = count_subaction_paths, + .subaction_paths = subaction_paths, + .bindings = binding_infos, }; - return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + + MEM_freeN(binding_infos); + return ret; } void WM_xr_action_binding_destroy(wmXrData *xr, const char *action_set_name, - const char *profile_path, const char *action_name, - unsigned int count_interaction_paths, - const char **interaction_paths) + const char *profile_path) { - GHOST_XrActionBindingInfo binding_info = { - .action_name = action_name, - .count_interaction_paths = count_interaction_paths, - .interaction_paths = interaction_paths, - }; - - GHOST_XrActionProfileInfo profile_info = { - .profile_path = profile_path, - .count_bindings = 1, - .bindings = &binding_info, - }; - - GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + GHOST_XrDestroyActionBindings( + xr->runtime->context, action_set_name, 1, &action_name, &profile_path); } bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) @@ -371,46 +393,64 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name) } { - /* Unset active modal action (if any). */ + /* Clear any active modal/haptic actions. */ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set; if (active_action_set) { - wmXrAction *active_modal_action = active_action_set->active_modal_action; - if (active_modal_action) { - if (active_modal_action->active_modal_path) { - active_modal_action->active_modal_path = NULL; - } - active_action_set->active_modal_action = NULL; - } + BLI_freelistN(&active_action_set->active_modal_actions); + BLI_freelistN(&active_action_set->active_haptic_actions); } } xr->runtime->session_state.active_action_set = action_set; - if (action_set->controller_pose_action) { - wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr); + if (action_set->controller_grip_action && action_set->controller_aim_action) { + wm_xr_session_controller_data_populate( + action_set->controller_grip_action, action_set->controller_aim_action, xr); + } + else { + wm_xr_session_controller_data_clear(&xr->runtime->session_state); } return true; } -bool WM_xr_controller_pose_action_set(wmXrData *xr, - const char *action_set_name, - const char *action_name) +bool WM_xr_controller_pose_actions_set(wmXrData *xr, + const char *action_set_name, + const char *grip_action_name, + const char *aim_action_name) { wmXrActionSet *action_set = action_set_find(xr, action_set_name); if (!action_set) { return false; } - wmXrAction *action = action_find(xr, action_set_name, action_name); - if (!action) { + wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name); + if (!grip_action) { return false; } - action_set->controller_pose_action = action; + wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name); + if (!aim_action) { + return false; + } + + /* Ensure consistent subaction paths. */ + const unsigned int count = grip_action->count_subaction_paths; + if (count != aim_action->count_subaction_paths) { + return false; + } + + for (unsigned int i = 0; i < count; ++i) { + if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) { + return false; + } + } + + action_set->controller_grip_action = grip_action; + action_set->controller_aim_action = aim_action; if (action_set == xr->runtime->session_state.active_action_set) { - wm_xr_session_controller_data_populate(action, xr); + wm_xr_session_controller_data_populate(grip_action, aim_action, xr); } return true; @@ -427,12 +467,12 @@ bool WM_xr_action_state_get(const wmXrData *xr, return false; } - BLI_assert(action->type == (eXrActionType)r_state->type); + r_state->type = (int)action->type; /* Find the action state corresponding to the subaction path. */ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { if (STREQ(subaction_path, action->subaction_paths[i])) { - switch ((eXrActionType)r_state->type) { + switch (action->type) { case XR_BOOLEAN_INPUT: r_state->state_boolean = ((bool *)action->states)[i]; break; @@ -462,19 +502,28 @@ bool WM_xr_action_state_get(const wmXrData *xr, bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, + const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude) { - return GHOST_XrApplyHapticAction( - xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ? + return GHOST_XrApplyHapticAction(xr->runtime->context, + action_set_name, + action_name, + subaction_path, + duration, + frequency, + amplitude) ? true : false; } -void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name) +void WM_xr_haptic_action_stop(wmXrData *xr, + const char *action_set_name, + const char *action_name, + const char *subaction_path) { - GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name); + GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path); } /** \} */ /* XR-Action API */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c new file mode 100644 index 00000000000..f9ad34b5a9b --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -0,0 +1,567 @@ +/* + * 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 Action Maps + * + * XR actionmap API, similar to WM keymap API. + */ + +#include <math.h> +#include <string.h> + +#include "BKE_context.h" +#include "BKE_idprop.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "GHOST_Types.h" + +#include "MEM_guardedalloc.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap" +#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action" +#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding" + +/* -------------------------------------------------------------------- */ +/** \name Action Map Binding + * + * Binding in an XR action map item, that maps an action to an XR input. + * \{ */ + +XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami, + const char *name, + bool replace_existing) +{ + XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name); + if (amb_prev && replace_existing) { + return amb_prev; + } + + XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__); + BLI_strncpy(amb->name, name, MAX_NAME); + if (amb_prev) { + WM_xr_actionmap_binding_ensure_unique(ami, amb); + } + + BLI_addtail(&ami->bindings, amb); + + /* Set non-zero threshold by default. */ + amb->float_threshold = 0.3f; + + return amb; +} + +static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami, + const char *name, + XrActionMapBinding *ambexcept) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME) && (amb != ambexcept)) { + return amb; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map bindings. + */ +void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, amb->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_binding_find_except(ami, name, amb)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(amb->name, name, MAX_NAME); +} + +static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src); + + amb_dst->prev = amb_dst->next = NULL; + + return amb_dst; +} + +XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, + XrActionMapBinding *amb_src) +{ + XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src); + + WM_xr_actionmap_binding_ensure_unique(ami, amb_dst); + + BLI_addtail(&ami->bindings, amb_dst); + + return amb_dst; +} + +bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) +{ + int idx = BLI_findindex(&ami->bindings, amb); + + if (idx != -1) { + BLI_freelinkN(&ami->bindings, amb); + + if (BLI_listbase_is_empty(&ami->bindings)) { + ami->selbinding = -1; + } + else { + if (idx <= ami->selbinding) { + if (--ami->selbinding < 0) { + ami->selbinding = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + if (STREQLEN(name, amb->name, MAX_NAME)) { + return amb; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map Item + * + * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output. + * \{ */ + +static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) +{ + WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op); + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); +} + +static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami) +{ + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + ami->op_properties_ptr = NULL; + ami->op_properties = NULL; + } + else { + BLI_assert(ami->op_properties == NULL); + } +} + +/** + * Similar to #wm_xr_actionmap_item_properties_set() + * but checks for the #eXrActionType and #wmOperatorType having changed. + */ +void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) +{ + switch (ami->type) { + case XR_BOOLEAN_INPUT: + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: + break; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + wm_xr_actionmap_item_properties_free(ami); + memset(ami->op, 0, sizeof(ami->op)); + return; + } + + if (ami->op[0] == 0) { + wm_xr_actionmap_item_properties_free(ami); + return; + } + + if (ami->op_properties_ptr == NULL) { + wm_xr_actionmap_item_properties_set(ami); + } + else { + wmOperatorType *ot = WM_operatortype_find(ami->op, 0); + if (ot) { + if (ot->srna != ami->op_properties_ptr->type) { + /* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */ + WM_operator_properties_create_ptr(ami->op_properties_ptr, ot); + if (ami->op_properties) { + ami->op_properties_ptr->data = ami->op_properties; + } + WM_operator_properties_sanitize(ami->op_properties_ptr, 1); + } + } + else { + wm_xr_actionmap_item_properties_free(ami); + } + } +} + +XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, + const char *name, + bool replace_existing) +{ + XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name); + if (ami_prev && replace_existing) { + wm_xr_actionmap_item_properties_free(ami_prev); + return ami_prev; + } + + XrActionMapItem *ami = MEM_callocN(sizeof(XrActionMapItem), __func__); + BLI_strncpy(ami->name, name, MAX_NAME); + if (ami_prev) { + WM_xr_actionmap_item_ensure_unique(actionmap, ami); + } + ami->selbinding = -1; + + BLI_addtail(&actionmap->items, ami); + + /* Set type to float (button) input by default. */ + ami->type = XR_FLOAT_INPUT; + + return ami; +} + +static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap, + const char *name, + const XrActionMapItem *amiexcept) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) { + return ami; + } + } + return NULL; +} + +/** + * Ensure unique name among all action map items. + */ +void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, ami->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(ami->name, name, MAX_NAME); +} + +static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami) +{ + XrActionMapItem *amin = MEM_dupallocN(ami); + + amin->prev = amin->next = NULL; + + if (amin->op_properties) { + amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr"); + WM_operator_properties_create(amin->op_properties_ptr, amin->op); + + amin->op_properties = IDP_CopyProperty(amin->op_properties); + amin->op_properties_ptr->data = amin->op_properties; + } + else { + amin->op_properties = NULL; + amin->op_properties_ptr = NULL; + } + + return amin; +} + +XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src) +{ + XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src); + + WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst); + + BLI_addtail(&actionmap->items, ami_dst); + + return ami_dst; +} + +bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) +{ + int idx = BLI_findindex(&actionmap->items, ami); + + if (idx != -1) { + if (ami->op_properties_ptr) { + WM_operator_properties_free(ami->op_properties_ptr); + MEM_freeN(ami->op_properties_ptr); + } + BLI_freelinkN(&actionmap->items, ami); + + if (BLI_listbase_is_empty(&actionmap->items)) { + actionmap->selitem = -1; + } + else { + if (idx <= actionmap->selitem) { + if (--actionmap->selitem < 0) { + actionmap->selitem = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + if (STREQLEN(name, ami->name, MAX_NAME)) { + return ami; + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Action Map + * + * List of XR action map items. + * \{ */ + +XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing) +{ + XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name); + if (am_prev && replace_existing) { + WM_xr_actionmap_clear(am_prev); + return am_prev; + } + + XrActionMap *am = MEM_callocN(sizeof(struct XrActionMap), __func__); + BLI_strncpy(am->name, name, MAX_NAME); + if (am_prev) { + WM_xr_actionmap_ensure_unique(runtime, am); + } + am->selitem = -1; + + BLI_addtail(&runtime->actionmaps, am); + + return am; +} + +static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime, + const char *name, + const XrActionMap *am_except) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) { + return am; + } + } + + return NULL; +} + +/** + * Ensure unique name among all action maps. + */ +void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + char name[MAX_NAME]; + char *suffix; + size_t baselen; + size_t idx = 0; + + BLI_strncpy(name, actionmap->name, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + + while (wm_xr_actionmap_find_except(runtime, name, actionmap)) { + if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) { + /* Use default base name. */ + BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME); + baselen = BLI_strnlen(name, MAX_NAME); + suffix = &name[baselen]; + idx = 0; + } + else { + BLI_snprintf(suffix, MAX_NAME, "%zu", idx); + } + } + + BLI_strncpy(actionmap->name, name, MAX_NAME); +} + +static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src) +{ + XrActionMap *am_dst = MEM_dupallocN(am_src); + + am_dst->prev = am_dst->next = NULL; + BLI_listbase_clear(&am_dst->items); + + LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) { + XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami); + BLI_addtail(&am_dst->items, ami_new); + } + + return am_dst; +} + +XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src) +{ + XrActionMap *am_dst = wm_xr_actionmap_copy(am_src); + + WM_xr_actionmap_ensure_unique(runtime, am_dst); + + BLI_addtail(&runtime->actionmaps, am_dst); + + return am_dst; +} + +bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) +{ + int idx = BLI_findindex(&runtime->actionmaps, actionmap); + + if (idx != -1) { + WM_xr_actionmap_clear(actionmap); + BLI_freelinkN(&runtime->actionmaps, actionmap); + + if (BLI_listbase_is_empty(&runtime->actionmaps)) { + runtime->actactionmap = runtime->selactionmap = -1; + } + else { + if (idx <= runtime->actactionmap) { + if (--runtime->actactionmap < 0) { + runtime->actactionmap = 0; + } + } + if (idx <= runtime->selactionmap) { + if (--runtime->selactionmap < 0) { + runtime->selactionmap = 0; + } + } + } + + return true; + } + + return false; +} + +XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + if (STREQLEN(name, am->name, MAX_NAME)) { + return am; + } + } + return NULL; +} + +void WM_xr_actionmap_clear(XrActionMap *actionmap) +{ + LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { + wm_xr_actionmap_item_properties_free(ami); + } + + BLI_freelistN(&actionmap->items); + + actionmap->selitem = -1; +} + +void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) +{ + LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { + WM_xr_actionmap_clear(am); + } + + BLI_freelistN(&runtime->actionmaps); + + runtime->actactionmap = runtime->selactionmap = -1; +} + +ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime) +{ + return &runtime->actionmaps; +} + +short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->actactionmap; +} + +void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->actactionmap = idx; +} + +short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime) +{ + return runtime->selactionmap; +} + +void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx) +{ + BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); + runtime->selactionmap = idx; +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 4ac05e339b9..bbb73fc2007 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -38,18 +38,18 @@ #include "wm_surface.h" #include "wm_xr_intern.h" -void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]) +void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) { - float iquat[4]; - invert_qt_qt_normalized(iquat, pose->orientation_quat); - quat_to_mat4(r_viewmat, iquat); - translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]); + quat_to_mat4(r_mat, pose->orientation_quat); + copy_v3_v3(r_mat[3], pose->position); } -void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) +void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) { - quat_to_mat4(r_mat, pose->orientation_quat); - copy_v3_v3(r_mat[3], pose->position); + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_imat, iquat); + translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); } static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, @@ -59,6 +59,7 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, float r_proj_mat[4][4]) { GHOST_XrPose eye_pose; + float eye_inv[4][4], base_inv[4][4]; copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); @@ -69,6 +70,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs); } + 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); + perspective_m4_fov(r_proj_mat, draw_view->fov.angle_left, draw_view->fov.angle_right, @@ -76,15 +83,6 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, draw_view->fov.angle_down, session_settings->clip_start, session_settings->clip_end); - - float eye_mat[4][4]; - float base_mat[4][4]; - - wm_xr_pose_to_viewmat(&eye_pose, eye_mat); - /* Calculate the base pose matrix (in world space!). */ - wm_xr_pose_to_viewmat(&draw_data->base_pose, base_mat); - - mul_m4_m4m4(r_view_mat, eye_mat, base_mat); } static void wm_xr_draw_viewport_buffers_to_active_framebuffer( diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 6415f96e322..4d4df43f796 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -26,19 +26,6 @@ struct wmXrActionSet; -typedef struct wmXrControllerData { - /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). - This subaction path will later be combined with a component path, and that combined path should - also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = - /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). - */ - char subaction_path[64]; - /** Last known controller pose (in world space) stored for queries. */ - GHOST_XrPose pose; - /** The last known controller matrix, calculated from above's controller pose. */ - float mat[4][4]; -} wmXrControllerData; - typedef struct wmXrSessionState { bool is_started; @@ -65,7 +52,7 @@ typedef struct wmXrSessionState { bool is_view_data_set; /** Last known controller data. */ - wmXrControllerData controllers[2]; + ListBase controllers; /* wmXrController */ /** The currently active action set that will be updated on calls to * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and @@ -83,6 +70,10 @@ typedef struct wmXrRuntimeData { /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; + + ListBase actionmaps; /* XrActionMap */ + short actactionmap; + short selactionmap; } wmXrRuntimeData; typedef struct wmXrViewportPair { @@ -112,6 +103,22 @@ typedef struct wmXrDrawData { float eye_position_ofs[3]; /* Local/view space. */ } wmXrDrawData; +typedef struct wmXrController { + struct wmXrController *next, *prev; + /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). + This subaction path will later be combined with a component path, and that combined path should + also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = + /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). + */ + char subaction_path[64]; + /* Pose (in world space) that represents the user's hand when holding the controller.*/ + GHOST_XrPose grip_pose; + float grip_mat[4][4]; + /* Pose (in world space) that represents the controller's aiming source. */ + GHOST_XrPose aim_pose; + float aim_mat[4][4]; +} wmXrController; + typedef struct wmXrAction { char *name; eXrActionType type; @@ -122,28 +129,47 @@ typedef struct wmXrAction { /** Previous states, stored to determine XR events. */ void *states_prev; - /** Input threshold for float/vector2f actions. */ - float float_threshold; + /** Input thresholds/regions for each subaction path. */ + float *float_thresholds; + eXrAxisFlag *axis_flags; /** The currently active subaction path (if any) for modal actions. */ - char **active_modal_path; + const char *active_modal_path; /** Operator to be called on XR events. */ struct wmOperatorType *ot; IDProperty *op_properties; + + /** Haptics. */ + char *haptic_name; + int64_t haptic_duration; + float haptic_frequency; + float haptic_amplitude; + + /** Flags. */ eXrOpFlag op_flag; + eXrActionFlag action_flag; + eXrHapticFlag haptic_flag; } wmXrAction; +typedef struct wmXrHapticAction { + struct wmXrHapticAction *next, *prev; + wmXrAction *action; + const char *subaction_path; + int64_t time_start; +} wmXrHapticAction; + typedef struct wmXrActionSet { char *name; - /** The XR pose action that determines the controller - * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose", - * although it could differ depending on the specification and hardware. */ - wmXrAction *controller_pose_action; + /** XR pose actions that determine the controller grip/aim transforms. */ + wmXrAction *controller_grip_action; + wmXrAction *controller_aim_action; - /** The currently active modal action (if any). */ - wmXrAction *active_modal_action; + /** Currently active modal actions. */ + ListBase active_modal_actions; + /** Currently active haptic actions. */ + ListBase active_haptic_actions; } wmXrActionSet; wmXrRuntimeData *wm_xr_runtime_data_create(void); @@ -164,10 +190,11 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); void wm_xr_session_actions_init(wmXrData *xr); void wm_xr_session_actions_update(wmXrData *xr); -void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, +void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, + const wmXrAction *aim_action, wmXrData *xr); void wm_xr_session_controller_data_clear(wmXrSessionState *state); -void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]); -void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 252f358c798..ba30b0dd864 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -63,6 +63,16 @@ static void wm_xr_session_create_cb(void) wm_xr_session_actions_init(xr_data); } +static void wm_xr_session_controller_data_free(wmXrSessionState *state) +{ + BLI_freelistN(&state->controllers); +} + +static void wm_xr_session_data_free(wmXrSessionState *state) +{ + wm_xr_session_controller_data_free(state); +} + static void wm_xr_session_exit_cb(void *customdata) { wmXrData *xr_data = customdata; @@ -74,6 +84,7 @@ static void wm_xr_session_exit_cb(void *customdata) } /* Free the entire runtime data (including session state and context), to play safe. */ + wm_xr_session_data_free(&xr_data->runtime->session_state); wm_xr_runtime_data_free(&xr_data->runtime); } @@ -338,7 +349,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, 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_viewmat(&viewer_pose, state->viewer_viewmat); + wm_xr_pose_to_imat(&viewer_pose, 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, @@ -398,32 +409,71 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, return true; } -bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr, unsigned int subaction_idx, float r_location[3]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || - subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { zero_v3(r_location); return false; } - copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position); + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_v3_v3(r_location, controller->grip_pose.position); return true; } -bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr, +bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || - subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) { + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { unit_qt(r_rotation); return false; } - copy_v4_v4(r_rotation, - xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat); + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_qt_qt(r_rotation, controller->grip_pose.orientation_quat); + return true; +} + +bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { + zero_v3(r_location); + return false; + } + + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_v3_v3(r_location, controller->aim_pose.position); + return true; +} + +bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, + unsigned int subaction_idx, + float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set || + (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) { + unit_qt(r_rotation); + return false; + } + + const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers, + subaction_idx); + BLI_assert(controller); + copy_qt_qt(r_rotation, controller->aim_pose.orientation_quat); return true; } @@ -443,16 +493,34 @@ void wm_xr_session_actions_init(wmXrData *xr) GHOST_XrAttachActionSets(xr->runtime->context); } -static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings, - const wmXrAction *controller_pose_action, +static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, + const float view_ofs[3], + const float base_mat[4][4], + GHOST_XrPose *r_pose, + float r_mat[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. */ + sub_v3_v3(m[3], view_ofs); + mul_m4_m4m4(r_mat, base_mat, m); + + /* Save final pose. */ + mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); +} + +static void wm_xr_session_controller_data_update(const XrSessionSettings *settings, + const wmXrAction *grip_action, + const wmXrAction *aim_action, wmXrSessionState *state) { - const unsigned int count = (unsigned int)min_ii( - (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers)); + BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); + BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); - float view_ofs[3]; - float base_inv[4][4]; - float tmp[4][4]; + unsigned int subaction_idx = 0; + float view_ofs[3], base_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { copy_v3_v3(view_ofs, state->prev_local_pose.position); @@ -464,22 +532,19 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin add_v3_v3(view_ofs, state->prev_eye_position_ofs); } - wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv); - invert_m4(base_inv); - - for (unsigned int i = 0; i < count; ++i) { - wmXrControllerData *controller = &state->controllers[i]; - - /* Calculate controller matrix in world space. */ - wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp); - - /* Apply eye position and base pose offsets. */ - sub_v3_v3(tmp[3], view_ofs); - mul_m4_m4m4(controller->mat, base_inv, tmp); + wm_xr_pose_to_mat(&state->prev_base_pose, base_mat); - /* Save final pose. */ - mat4_to_loc_quat( - controller->pose.position, controller->pose.orientation_quat, controller->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, + &controller->grip_pose, + controller->grip_mat); + wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], + view_ofs, + base_mat, + &controller->aim_pose, + controller->aim_mat); } } @@ -498,33 +563,44 @@ void wm_xr_session_actions_update(wmXrData *xr) return; } - /* Only update controller mats for active action set. */ + /* Only update controller data for active action set. */ if (active_action_set) { - if (active_action_set->controller_pose_action) { - wm_xr_session_controller_mats_update( - &xr->session_settings, active_action_set->controller_pose_action, state); + if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { + wm_xr_session_controller_data_update(&xr->session_settings, + active_action_set->controller_grip_action, + active_action_set->controller_aim_action, + state); } } } -void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr) +void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, + const wmXrAction *aim_action, + wmXrData *xr) { + UNUSED_VARS(aim_action); /* Only used for asserts. */ + wmXrSessionState *state = &xr->runtime->session_state; + ListBase *controllers = &state->controllers; - const unsigned int count = (unsigned int)min_ii( - (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths); + BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); + const unsigned int count = grip_action->count_subaction_paths; + + wm_xr_session_controller_data_free(state); for (unsigned int i = 0; i < count; ++i) { - wmXrControllerData *c = &state->controllers[i]; - strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]); - memset(&c->pose, 0, sizeof(c->pose)); - zero_m4(c->mat); + wmXrController *controller = MEM_callocN(sizeof(*controller), __func__); + + BLI_assert(STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])); + strcpy(controller->subaction_path, grip_action->subaction_paths[i]); + + BLI_addtail(controllers, controller); } } void wm_xr_session_controller_data_clear(wmXrSessionState *state) { - memset(state->controllers, 0, sizeof(state->controllers)); + wm_xr_session_controller_data_free(state); } /** \} */ /* XR-Session Actions */ @@ -581,9 +657,6 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, GPUViewport *viewport = vp->viewport; const bool size_changed = offscreen && (GPU_offscreen_width(offscreen) != draw_view->width) && (GPU_offscreen_height(offscreen) != draw_view->height); - char err_out[256] = "unknown"; - bool failure = false; - if (offscreen) { BLI_assert(viewport); @@ -594,8 +667,29 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, GPU_offscreen_free(offscreen); } + char err_out[256] = "unknown"; + bool failure = false; + eGPUTextureFormat format = + GPU_R8; /* Initialize with some unsupported format to check following switch statement. */ + + switch (draw_view->swapchain_format) { + case GHOST_kXrSwapchainFormatRGBA8: + format = GPU_RGBA8; + break; + case GHOST_kXrSwapchainFormatRGBA16: + format = GPU_RGBA16; + break; + case GHOST_kXrSwapchainFormatRGBA16F: + format = GPU_RGBA16F; + break; + case GHOST_kXrSwapchainFormatRGB10_A2: + format = GPU_RGB10_A2; + break; + } + BLI_assert(format != GPU_R8); + offscreen = vp->offscreen = GPU_offscreen_create( - draw_view->width, draw_view->height, true, false, err_out); + draw_view->width, draw_view->height, true, format, err_out); if (offscreen) { viewport = vp->viewport = GPU_viewport_create(); if (!viewport) { |